I'm trying encrypt data with JSEncrypt (v 2.3.1) then server decrypt data (Java).
Most of time it works, but sometime backend throw BadPaddingException.
I found on stack it may be duplicated with this question:
JSEncrypt(js) encrypt, but python cannot decrypt but seem it not same exception.
I googled and I found the issue: JSEncrypt#encrypt creates sporadically invalid ciphertexts. But when I tried reproduced issue follow the github link, backend code still decrypt data.
Could anyone give some suggestions ?
JS code:
function encryptInput(input,key){
var encrypt = new JSEncrypt();
encrypt.setPublicKey(key);
var encrypted = encrypt.encrypt(input);
return encrypted;
}
Java code:
public String decryptWeb(String cipherText, String privateKey){
try{
// Remove the first and last lines
privateKey = privateKey.replace("-----BEGIN PRIVATE KEY-----", "");
privateKey = privateKey.replace("-----END PRIVATE KEY-----", "");
// Base64 decode data
byte[] encoded = Base64.decodeBase64(privateKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey privateKey = (RSAPrivateKey) kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8"); // exception throw here
}catch(Exception ex){
LOG.error("decrypt",ex);
return "";
}
}
Exception:
decrypt - decrypt
javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at com.util.Decryptor.decrypt(Decryptor.java:161)
Related
This error is already discussed in several links.
I got the similar kind of errors, But my scenario is little different than others.
I need
only Decryption. not Encryption.
In my case, an encrypted message is already stored in the DB. I got that as String and now need to only decrypt it to get the original String.
Any suggestions will be very helpful.
My piece of code is as follows:
String encodedStr = "c2J7RiP1CY++gfdE7Ke/xD1fSOOdO7DvprdZZUDws9Yl8DmYJV64zGV9vvK/TMEfALqSPH8KcZik8XvmLowpjJWzYY2Of1aytHaya0ULwmopklRWlkaS5sjL80jjyhaRN2O+zQNarbAQy8g3VtXX643T2AhDTsT+NKFsCcpH4xbqmViwD1GXQQLsLwuKZx1POAtyC0UaMjQ2SrzR+eVEiSgKPDABDKIRbOyBEXRcJMK/t/P7uIk9tJ/p1X2JqQOO7GMO/1x7rvF6Pb1Fik2tmQv5qL1W6/kV97/VT5Hpi9uh6zdCFO7sstYvgkxxbs3gTyJ5raWlATVU6a/0B/Q50w==";
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
Cipher cipher = Cipher.getInstance("RSA");
// Encryption is not required.
//cipher.init(Cipher.ENCRYPT_MODE, privateKey);
//String ss =Base64.encodeBase64String(cipher.doFinal(encodedStr.getBytes("UTF-8")));
//System.out.println(ss);
// Only decryption is required.
cipher.init(Cipher.DECRYPT_MODE, publicKey);
String sss = new String(cipher.doFinal(Base64.decodeBase64(encodedStr)), "UTF-8");
System.out.println(sss);
And I'm getting below exception everytime at the line cipher.doFinal(Base64.decodeBase64(encodedStr) :
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:383)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:294)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at com.sprint.neo.bc4j.util.TestMain.main(TestMain.java:20)
Though I don't require encryption, the Cipher.ENCRYPT_MODE is working fine.
But incase of Cipher.DECRYPT_MODE , it's throwing exception.
Please suggest me, how can I only decrypt an already existing encrypted String from DB, to get the original String value.
Here is the piece of code which works:
public static void main(String[] args) {
byte[] txt = "message".getBytes();
byte[] encText;
try{
// Load the keystore
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = "keystorePassword".toCharArray();
Key rsakey = ks.getKey("mykeyalias", password);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Encrypt
Certificate cert = ks.getCertificate("mykeyalias");
try
{
cipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
encText = cipher.doFinal(txt);
System.out.println(encText.toString());
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, rsakey);
String decrypted = new String(cipher.doFinal(encText));
System.out.println(decrypted);
} catch (Exception e) {
System.out.println("error" + e);
}
}
Your publicKey generated will be different each time and hence the encrypted String will be different. The string generated will no longer be valid for decryption.
Try to read key secret from a file and use same encrypted string to decrypt.
I am trying to integrate an API where I have to send the data in RSA encrypted and BASE64 encoded form. In response I get a BASE64 String which is supposed to be decoded and decrypted at my end. I am able to send the data in encrypted and encoded form. Also I am getting the response BASE64 String but I am NOT ABLE TO DECRYPT the string. In my decryption I am getting following error:
Exception in thread "main" java.lang.RuntimeException: javax.crypto.BadPaddingException: Decryption error
I have used following for encryption:
String result=new Gson().toJson(parameters);
byte[] cleartext = result.getBytes("UTF-8");
PublicKey publicKey=readPublicKey(path);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] ciphertext=cipher.doFinal(cleartext);
String securePayload = Base64.getEncoder().encodeToString(ciphertext);
Parameters is a (String,String) map and I have put the parameters in it that are required to POST in the API. After encryption I am sending "securePayload" in POST request and in response I am getting a BASE64 encoded string which I am supposed to decode and decrypt.
For decryption I am using following code:
public static String getDecrypted(String data, PrivateKey key) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] dataArray=Base64.getDecoder().decode(data);
byte[] decryptedbytes = cipher.doFinal(dataArray);
return new String(decryptedbytes);
}
Above function I am using in following code to decrypt the String "data" which I am getting in API response:
PrivateKey pvtKey= readFromPrivateKey("C:\\Users\\Administrator\\Desktop\\myPrivateKey.key");
String result=getDecrypted(data, pvtKey);
To read the private key I have used following code:
public static PrivateKey readFromPrivateKey(String path) {
String privateKeyContent = new String(Files.readAllBytes(Paths.get(path)));
privateKeyContent = privateKeyContent.replaceAll(System.lineSeparator(), "").replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
System.out.println(privateKeyContent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyContent));
PrivateKey privKey = kf.generatePrivate(keySpecPKCS8);
return privKey;
}
Please help in the same. What wrong am I doing? I have checked that the Public and Private key pair used at my end is correct.
This might be a duplicate of this answered question, but I can't seem to get the same results. Hoping for some guidance here.
JSEncrypt (client)
let encrypt = new Encrypt.JSEncrypt();
encrypt.setPublicKey(this.publicKey); // retrieved from server
encrypt.encrypt(password);
BouncyCastle (server) - RSA key generation
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(1024);
KeyPair pair = generator.generateKeyPair();
PublicKey pubKey = pair.getPublic();
PrivateKey privKey = pair.getPrivate();
// returned to client
String publicKeyStr = new String(Base64.encodeBase64(pubKey.getEncoded()));
String privateKeyStr = new String(Base64.encodeBase64(privKey.getEncoded()));
BouncyCastle (server) - Decryption
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// org.apache.commons.codec.binary.Hex
byte[] cipherText = cipher.doFinal(Hex.decodeHex(encrypted.toCharArray()));
decrypted = new String(cipherText, BaseConstant.ENC_UTF8);
Error
org.apache.commons.codec.DecoderException: Illegal hexadecimal character I at index 0
at org.apache.commons.codec.binary.Hex.toDigit(Hex.java:178)
at org.apache.commons.codec.binary.Hex.decodeHex(Hex.java:89)
One thing I noticed is the length of encrypted text by JSEncrypt, which is 172, while encryption at server side produces 256.
The answered question mentioned to use RSA/None/PKCS1Padding, which I had already set. What else could I be missing?
The error occurs in Hex.decodeHex() method, which means that your data is not a Hex encoded string.
JSEncrypt.encrypt() method returns the encrypted data in Base64 (instead of Hex string). In order to decrypt it, you must decode it from base64 format.
So instead of:
byte[] cipherText = cipher.doFinal(Hex.decodeHex(encrypted.toCharArray()));
Do this:
byte[] cipherText = cipher.doFinal(Base64.decodeBase64(encrypted.toCharArray()));
You can also solve this problem just from the client side. See the code below:
let encrypt = new Encrypt.JSEncrypt();
encrypt.setPublicKey(this.publicKey);
encrypt.getKey().encrypt(password);
Just add getKey() after encrypt. It worked for me! I encrypted my password into Hex string using this approach.
I am trying to encript a string in an Android app with a public key that I get from my php server.
php server code:
$res = openssl_pkey_new();
openssl_pkey_export($res, $privKey);
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
echo $pubKey;
The server create correctly the key. In my app I store the public key in "response" variable. This var is like:
response = "-----BEGIN PUBLIC KEY-----MIIB... etc ...wIDAQAB-----END PUBLIC KEY-----"
Android code:
String pass = "password";
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(response.trim().getBytes(), Base64.DEFAULT));
Key publicKey = keyFac.generatePublic(keySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherText = cipher.doFinal(pass.getBytes());
strEncryInfoData = new String(Base64.encode(cipherText,Base64.DEFAULT));
} catch (Exception e){
}
When I run the application, appear this error:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
I don't know why the code doesn't encript the password. Any idea? It is about the encode type?
Thanks.
Try removing the BEGIN header and END footer and all the newlines, then you get the Base64 encoded DER which you need to decode and pass to the X509EncodedKeySpec constructor. Here's some code:
response = response.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
response = response.replace("\n", "").replace("\r", "");
// Use Base64.NO_WRAP because: https://stackoverflow.com/questions/32935783/java-different-results-when-decoding-base64-string-with-java-util-base64-vs-and/32935972
byte[] responseBytes = Base64.decode(response, Base64.NO_WRAP);
KeySpec keySpec = new X509EncodedKeySpec(responseBytes);
I have RSA Public-Key on server side in below format:
<string xmlns="http://www.cherripik.com/">
<RSAKeyValue><Modulus>abc</Modulus><Exponent>abc</Exponent></RSAKeyValue>
</string>
I have tried almost all possible ways but could not able to encrypt string with this public key on android side. Could anyone give me an example in which i will encrypt any of the string like "abc" with this public key and also decrypt that encrypted key to back to "abc". It will be very helpful to me.
Advance Thanks.
Below are the ways which i have used but no success. It gave some value but it is not correct.
public String encrypt(String message, String Modulus, String Exponent) {
String outputEncrypted = "";
try {
byte[] modulusBytes = Base64Coder.decode(Modulus);
byte[] exponentBytes = Base64Coder.decode(Exponent);
BigInteger modulus = new BigInteger(modulusBytes );
BigInteger exponent = new BigInteger(exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = new String("abc").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal( plainBytes );
String encryptedString = new String(Base64Coder.encode(cipherData));
Log.i(this.getClass().getSimpleName(), "encryptedString : "+encryptedString);
} catch (Exception e) {
// TODO: handle exception
}
return outputEncrypted;
}
One more thing when I create encrypted string with above method. It will give 346 characters encrypted string. But on my server, I have only encrypt and decrypt method. On server encrypt method, it will producing 344 character. Lastly when I put my encrypted string to server method to verify that my encrypted string is correct. Server throw this error.
<string xmlns="http://www.Myserver.com/">Error occurred while decoding OAEP padding.</string>