I'm trying to convert an RSA pem key (contained in a String) to a byte[], like this method does when given a .pem file FileInputStream:
http://jets3t.s3.amazonaws.com/api/org/jets3t/service/security/EncryptionUtil.html#convertRsaPemToDer(java.io.InputStream)
I've tried this:
String pemKey = "-----BEGIN RSA PRIVATE KEY-----\r\n"
+ "{base64 encoded key part omitted}\r\n"
+ "{base64 encoded key part omitted}\r\n"
+ "{base64 encoded key part omitted}\r\n"
+ "-----END RSA PRIVATE KEY-----";
String base64 = pemKey
.replaceAll("\\s", "")
.replace("-----BEGINRSAPRIVATEKEY-----", "")
.replace("-----ENDRSAPRIVATEKEY-----", "");
return Base64.decode(base64.getBytes());
I expect the result to be equivalent to what would be returned by org.jets3t.service.security.EncryptionUtil.convertRsaPemToDer() but it does not seem to be working when generating a CloudFront streaming URL.
Any idea what I'm doing wrong?
Just wrap the string in a ByteArrayInputStream and you can use the method you linked:
InputStream pemStream = new ByteArrayInputStream(pemKey.getBytes());
byte[] derKey = EncryptionUtil.convertRsaPemToDer(pemStream);
Related
I am trying to create a PKI signature using the private key in PHP.
These are the following rules to create a signature
Use the SHA-2 algorithm to generate the hash of the Signature Base String.
Sign the hashed value using the private key of the app.
Base64-encode the signature value.
NOTE: Base64 encoding should not include the CRLF (carriage return/line feed) every 72 characters
which is part of strict Base64 encoding. Instead, the whole Base64 encoded string should be without
line breaks.
Set the string as the value for the signature parameter.
Example of Nodejs code:
var signature = crypto.createSign('RSA-SHA256')
.update(baseString)
.sign(signWith, 'base64');
Java Code
String baseString = "Constructed base string";
Signature sig = Signature.getInstance("RSA-SHA256");
sig.initSign(privateKey); // Get private key from keystone
sig.update(baseString.getBytes());
byte[] signedData = sig.sign();
String finalStr = Base64.getEncoder().encodeToString(signedData);
I am trying to convert this code into PHP,
My base string is correct.
// $data = "BaseString";
// $private_key_pem = openssl_pkey_get_private("file://".$path."privateKey.pem",'passphrase');
$hash = hash('sha256', $data);
$result = openssl_sign($hash, $signature, $private_key_pem,'RSA-SHA256');
$signature = base64_encode($signature);
Is this correct?
If yes, The API response is "Invalid PKI signature"
I setup two programs in Java and PHP to compare the output (signature = finalStr) and
the verification of the signature. To get a comparable result I hardcoded the RSA keys
in both programs so the code looks like a little bit strange.
To get shorter key strings I generated 512 bit RSA keys that are unsecure -
use a minimum of 2048 bit keys in production.
As you can see both programs generate the same signature of:
finalStr: NEHC7o+mW34qoTNOwXRQIRfs80s/YhudzX0K4AGlFTeyyJcRhit9f03iw58Ww1Eo3zfkSrrz3411TZheVLHFnQ==
and both programs can verify the signature as true.
This is the Java code:
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class MainSo {
public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException {
System.out.println("https://stackoverflow.com/questions/62674669/converting-nodejs-or-java-signature-hashing-function-to-php");
// keys are sample rsa 512 keys
String privateKey1 = "-----BEGIN PRIVATE KEY-----\n" +
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqg8Hlhxm7LfqASjF\n" +
"KMce91anr2ViG/K8GQmk0HPMiw3Lh6DrGDGmsw2jUczwQTyv07qDwWwf+vaEiTdk\n" +
"jd1JxQIDAQABAkAOGbTtU2mNUyqJ8hF28hu1MnAw8N0TqCrEgLIzvoZFOTqvxPqc\n" +
"VaCuUs4Fm/J5x8gWLycsRmbBMeecIzvjzXY5AiEAtoZ4WSplvJbEHjiKhW+dRICc\n" +
"tSTcGaTf0v4vdfQTiGsCIQDug9wLUZDiSttbz2QlA3QthFX+UIu8fE/A/lGEjXnC\n" +
"jwIgcejRyrPO8jcVBdc7e7MAbvPk2Je8VLS0irTfYbmFRykCIQDCFsbu5vbxTlzm\n" +
"fwNNI1xc1b1sb3rmbHox4EHRjZaxfQIgEr2r53jmSRlyQfueo4nLZJhTGXdaJN8Z\n" +
"yoWwFsFqsiA=\n" +
"-----END PRIVATE KEY-----";
String publicKey1 = "-----BEGIN PUBLIC KEY-----\n" +
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoPB5YcZuy36gEoxSjHHvdWp69lYhvy\n" +
"vBkJpNBzzIsNy4eg6xgxprMNo1HM8EE8r9O6g8FsH/r2hIk3ZI3dScUCAwEAAQ==\n" +
"-----END PUBLIC KEY-----";
// rsa key generation
// Remove markers and new line characters in private key
String realPrivateKey = privateKey1.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] priKey = Base64.getDecoder().decode(realPrivateKey);
PKCS8EncodedKeySpec specPri = new PKCS8EncodedKeySpec(priKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(specPri);
// Remove markers and new line characters in public key
String realPublicKey = publicKey1.replaceAll("-----END PUBLIC KEY-----", "")
.replaceAll("-----BEGIN PUBLIC KEY-----", "")
.replaceAll("\n", "");
byte[] pubKey = Base64.getDecoder().decode(realPublicKey);
X509EncodedKeySpec specPub = new X509EncodedKeySpec(pubKey);
PublicKey publicKey = kf.generatePublic(specPub);
String baseString = "Constructed base string";
//Signature sig = Signature.getInstance("RSA-SHA256");
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey); // Get private key from keystone
sig.update(baseString.getBytes());
byte[] signedData = sig.sign();
String finalStr = Base64.getEncoder().encodeToString(signedData);
System.out.println("finalStr: " + finalStr);
// verify signature
byte[] signedDataVerify = Base64.getDecoder().decode(finalStr);
Signature sigVerify = Signature.getInstance("SHA256withRSA");
sigVerify.initVerify(publicKey);
sigVerify.update(baseString.getBytes());
boolean verified = sigVerify.verify(signedDataVerify);
System.out.println("signature verified: " + verified);
}
}
and here is the PHP-code:
<?php
// https://stackoverflow.com/questions/62674669/converting-nodejs-or-java-signature-hashing-function-to-php
$data = 'Constructed base string';
// sample 512 rsa keys
$privateKey1 = "-----BEGIN PRIVATE KEY-----\n" .
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqg8Hlhxm7LfqASjF\n" .
"KMce91anr2ViG/K8GQmk0HPMiw3Lh6DrGDGmsw2jUczwQTyv07qDwWwf+vaEiTdk\n" .
"jd1JxQIDAQABAkAOGbTtU2mNUyqJ8hF28hu1MnAw8N0TqCrEgLIzvoZFOTqvxPqc\n" .
"VaCuUs4Fm/J5x8gWLycsRmbBMeecIzvjzXY5AiEAtoZ4WSplvJbEHjiKhW+dRICc\n" .
"tSTcGaTf0v4vdfQTiGsCIQDug9wLUZDiSttbz2QlA3QthFX+UIu8fE/A/lGEjXnC\n" .
"jwIgcejRyrPO8jcVBdc7e7MAbvPk2Je8VLS0irTfYbmFRykCIQDCFsbu5vbxTlzm\n" .
"fwNNI1xc1b1sb3rmbHox4EHRjZaxfQIgEr2r53jmSRlyQfueo4nLZJhTGXdaJN8Z\n" .
"yoWwFsFqsiA=\n" .
"-----END PRIVATE KEY-----\n";
$publicKey1 = "-----BEGIN PUBLIC KEY-----\n" .
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoPB5YcZuy36gEoxSjHHvdWp69lYhvy\n" .
"vBkJpNBzzIsNy4eg6xgxprMNo1HM8EE8r9O6g8FsH/r2hIk3ZI3dScUCAwEAAQ==\n" .
"-----END PUBLIC KEY-----\n";
$privateKey = openssl_pkey_get_private ($privateKey1);
$publicKey = openssl_pkey_get_public($publicKey1);
// create the signature
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
echo 'finalStr (Base64):' . PHP_EOL . base64_encode($signature) . PHP_EOL;
// verify signature
$result = openssl_verify($data, $signature, $publicKey, "sha256WithRSAEncryption");
echo 'verified (0=false, 1=true): ' . $result;
?>
We want to Hash a data using clients digital signature using java sha 256 bit hashing algorithm.
How can we add digital signature while hashing in java.
If I'm understanding correctly, you want to sign some data. Here is a sample method:
public static String encode(String dataToEncode, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
byte[] decodedSecret = Base64.getDecoder().decode(secret);
SecretKeySpec keySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
Mac sha256 = Mac.getInstance("HmacSHA256");
sha256.init(keySpec);
return Base64.getEncoder().encodeToString(sha256.doFinal(dataToEncode.getBytes()));
}
The secret is the Base64 encoded secret key. The method returns the Base64 encoded hash of the data. The Base64 part is optional, you can remove it if you don't need that encoding. This is a method I use when signing REST API calls to crypto exchanges.
The following solution signs a String by applying an RSA PKCS#8 formatted private key. If your code has read the private key as a text from a pem file that looks like the following example:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRxFWXGYDG8zKw
ihIS+Ydh/nWX9NwkFTKMRjH8BQ78ZEnXrnGJHvd+dI+zEiRo7rLuDXMOjsnhIR/O
....
+wqssDAApq+CiPcBnn0x2Vw=
-----END PRIVATE KEY-----
Then you need to strip out the first and last lines and all the new line characters ('\n'). If your privateKey is read (from a java keystore for example) you can remove the lines of code that convert the String of private key into java.security.PrivateKey object.
private static String signSHA256RSA(String inputStr, String inputKey) throws Exception {
String key = inputKey.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] keyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(inputStr.getBytes("UTF-8"));
byte[] s = signature.sign();
return Base64.getEncoder().encodeToString(s);
}
I'm trying to create a private key object from a given .pem file. The file has this structure:
-----BEGIN EC PRIVATE KEY-----
...............................
...............................
...............................
-----END EC PRIVATE KEY-----
I am attempting to create the private key object with this code:
public static String getKeyFromFile(String filename) throws IOException {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String key = new String(keyBytes);
return key;
}
public static PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, NoSuchProviderException {
String privateKeyPEM = getKeyFromFile("MY_FILE.pem");
privateKeyPEM = privateKeyPEM.replace("-----BEGIN EC PRIVATE KEY-----\n", "");
privateKeyPEM = privateKeyPEM.replace("-----END EC PRIVATE KEY-----", "");
privateKeyPEM = privateKeyPEM.replaceAll("\n", "");
privateKeyPEM = privateKeyPEM.replaceAll(" ", "");
byte[] privateKeyBytes = privateKeyPEM.getBytes();
String encodedString = Base64.getEncoder().encodeToString(privateKeyBytes);
byte[] decodedString = Base64.getDecoder().decode(encodedString);
EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(decodedString);
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey privKey = kf.generatePrivate(privKeySpec);
return privKey;
Upon running this method, I receive this error:
java.security.InvalidKeyException: invalid key format
I am able to parse the text and strip away any unwanted characters just fine, but I'm not able to create the private key object. I am able to generate a public key object from a similar .crt file using very similar methods. I want to be able to do this solely within Java and no openssl. Any help would be greatly appreciated.
Your code does not properly decode the base64 data:
privateKeyPEM contains the String data between the BEGIN and END data (which is base64 encoded).
Your code does the following:
byte[] privateKeyBytes = privateKeyPEM.getBytes();
// privateKeyBytes now contains the base64 encoded key data
String encodedString = Base64.getEncoder().encodeToString(privateKeyBytes);
// encoded String contains now the base64 encoded data of the base64 encoded key data
byte[] decodedString = Base64.getDecoder().decode(encodedString);
// decodedString is not the base64 encoded data of your key data
Why are you encoding the data base64 and then in the next line decoding it - both steps together are just useless.
What you really need is to apply the base64 decode one time onto privateKeyPEM:
byte[] keyData = Base64.getDecoder().decode(privateKeyPEM);
EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(keyData);
If the base64 decode fails then your base64 data is invalid - most likely because of contained spaces or \r.
I have generated RSA public and private keys in my python app. I want to encrypt some string in python, send it to java app and decrypt it. How to do that? I've tried few methods described in here but I can't make it work:
https://stackoverflow.com/a/19387517/3626048
Getting RSA private key from PEM BASE64 Encoded private key file
Key from String in Java RSA
This is my python code:
from Crypto.PublicKey import RSA
new_key = RSA.generate(2048, e=65537)
public_key = new_key.publickey().exportKey("PEM")
private_key = new_key.exportKey("PEM")
def encrypt(string, public_key):
encrypted = public_key.encrypt(bytes(string, "latin1"), 255)
date = encrypted[0]
date = base64.b64encode(date)
return date
Decryption in python works:
def decrypt(string, private_key):
raw_cipher_data = base64.b64decode(string)
string = private_key.decrypt(raw_cipher_data)
string = string.decode("latin1")
return string
where I create private_key object like this:
private_key_str = "-----BEGIN RSA PRIVATE KEY-----\nMIIE+IB [.....] bcy/VVp63YA==\n-----END RSA PRIVATE KEY-----"
private_key = RSA.importKey(private_key_str)
I'm trying to sign a string using BouncyCastle library.
My code works, but the resulting string is full of weird characters and my instinct says something is wrong about it.
My code looks like this
Security.addProvider(new BouncyCastleProvider());
FileReader fileReader = new FileReader(new File("certs/private.pem"));
PEMReader r = new PEMReader(fileReader);
PrivateKey privateKey = (PrivateKey) r.readObject();
r.close()
String toSign = "hello world";
Signature signature = Signature.getInstance("SHA1withRSA","BC");
signature.initSign(privateKey);
signature.update(toSign.getBytes("UTF-8"));
byte[] signedArray = signature.sign();
String signedString = new String(signedArray, "UTF-8");
And the resulting string (signedString) looks (awfully) like this:
�����jc.������c�1�#�ٶ����E8����a��f8���t�~W�{%��\Z#��it��ҽ;�n��k�n{U>&�d�_���&�?�N��g�
z\�k�g���e~�S4��ƎG�g��U�:��s>i�%YL�n3�����Y��9����T���}�Usb���&�����eշѾUr�Y�ڝ[j�h~mu\3U��j���c�U�ac����t��No-��1J�B]�
The private.pem was generated with this command
openssl req -new -x509 -days 3652 -nodes -out private.crt -keyout private.pem.
Any help or hint will be very appreciated.
SOLVED
What I did was to encode de byte array to Base64 using this line
byte[] encodedArray = org.bouncycastle.util.encoders.Base64.encode(signedArray);
and voalá!
Your signature is a byte[], it is not a string. Attempting to treat a byte array as a string gives you what you have found. Either retain and store the signature as a byte array, or else convert the byte array to a string-compatible format, such as Base64. Java 8 contains the Base64 class which will do the conversion for you. If you do use Base64, then remember to convert back to bytes before checking the signature.