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.
Related
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)
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 a problem with decrypting a message using AES. At the end when I expect a message, e.g.
ala123
Instead of that I receive sth like:
...6�b}\7�k�8�vFP�8~%��_zժF��FW��O_e���ó������������ala123
The message I pass to encryption is built as:
cipher key is SHA-256 from AES_TOKEN
cipher IV is some characters, which are then stored in the message (at the beginnig)
decrypted message is wrapped up into Base64
The question is why at the end I eventually receive my expected message, but with a lot of rubbish chars at the beggining?
My encryption code is:
private static final String AES_TOKEN = "my_very_secret_token";
// encrypted is base64 string
public String decrypt(String encrypted) throws Exception {
byte[] decrypted = Base64.getDecoder().decode(encrypted);
return new String(aesDecrypt(decrypted), "UTF-8");
}
private byte[] aesDecrypt(byte[] message) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] token = MessageDigest.getInstance("SHA-256").digest(AES_TOKEN.getBytes());
SecretKeySpec secretKey = new SecretKeySpec(token, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(message, 16));
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
return cipher.doFinal(message);
}
It looks like you aren't removing the IV from the beginning of message after reading it in to iv. That would explain the garbage being at the start of the decrypted message.
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>
In my program, I'm trying to encrypt some plaintext with RSA using the following code:
static String RSAEncrypt(String pubkey, String plain){
return encrypt(pubkey,plain,"RSA");
}
static String encrypt(String stringKey, String plain, String algo){
String enc="failed";
try{
byte[] byteKey = new BASE64Decoder().decodeBuffer(stringKey);
Key key = new SecretKeySpec(byteKey,algo);
byte[] data = plain.getBytes();
Cipher c = Cipher.getInstance(algo);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(data);
enc = new BASE64Encoder().encode(encVal);
}catch(Exception e){e.printStackTrace();}
return enc;
}
However, when it runs, it shows the following error:
java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
at javax.crypto.Cipher.chooseProvider(Cipher.java:877)
at javax.crypto.Cipher.init(Cipher.java:1212)
at javax.crypto.Cipher.init(Cipher.java:1152)
at Crypto.encrypt(Crypto.java:37)
at Crypto.RSAEncrypt(Crypto.java:62)
I have tried changing it to RSA/None/PKCS1Padding and RSA/ECB/PKCS1Padding to no avail.. I know that installing BouncyCastle may help but I'd like to avoid it (I'd like to avoid more dependencies and I've been having some issues installing it anyway). Thanks in advance for any ideas.
As was said in the comments, SecretKeySpec is for symmetric algorithms only. You mentioned that you got your byte[] containing the key by calling getEncoded.
There are two possibilities and two resulting formats:
Encoding of an RSA PrivateKey
Calling PrivateKey#getEncoded on an instance of an RSA private key will result in a PKCS#8 encoding for private keys, and it can be restored with the help of PKCS8EncodedKeySpec.
Encoding of an RSA PublicKey
PublicKey#getEncoded on an RSA public key results in the generic X.509 public key encoding, and can be restored with X509EncodedKeySpec.
Example Usage
byte[] data = "test".getBytes("UTF8");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keyPair = kpg.genKeyPair();
byte[] pk = keyPair.getPublic().getEncoded();
X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(data);
byte[] priv = keyPair.getPrivate().getEncoded();
PKCS8EncodedKeySpec spec2 = new PKCS8EncodedKeySpec(priv);
PrivateKey privKey = keyFactory.generatePrivate(spec2);
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] plain = cipher.doFinal(encrypted);
System.out.println(new String(plain, "UTF8")); //=> "test"