how to use (RSA/ECB/PKCS1Padding) in android - java

About a year ago, I wrote an application for Android and used a class in it RSA In this class, there was the following code snippet and the application worked
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
But when I re-entered the application code, I did not open the new encrypted information to change the private key until I changed the above code line to the following code line.
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
The problem is that if I replace the above code snippet in class RSA it is no longer possible to open previously encrypted information (with the same keys as before).
And I see the following error
javax.crypto.BadPaddingException: error:04000084:RSA routines:OPENSSL_internal:PKCS_DECODING_ERROR
RSA decryption
public static byte[] decryptByPrivateKey(byte[] data, String key)
throws Exception {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}

RSA key pairs can be used within different RSA based schemes, such as PKCS#1 and OAEP padding for encryption, and PKCS#1 and PSS padding for signing. However, there is only one key pair generation possible, which is simply denoted "RSA".
If only "RSA" is used as input string it will use the defaults set for the specific cryptography provider, which is - in this case - the first provider that implements RSA using keys in software. Apparently that's different on Android from PKCS#1 padding (assuming that you still use the original list of providers, of course). One stupid thing in Java is that you cannot programmatically find out which defaults are used; getAlgorithm() ususally just returns the string you've provided earlier. The only thing you can do is to get the provider using getProvider() and then lookup the defaults...
I would never go for any defaults (except for SecureRandom defaults) as it is unspecified which defaults will be used for Java. Always specify the algorithm in full; your earlier string was fine.

My function
private fun getEncryptCodeWord(publicKey:String, codeWord:String):String{
try{
val publicBytes = Base64.decode(publicKey, Base64.NO_WRAP)
val keySpec = X509EncodedKeySpec(publicBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val pubKey = keyFactory.generatePublic(keySpec)
val encryptCodeWord = Cipher.getInstance("RSA/ECB/PKCS1Padding")
.apply { init(Cipher.ENCRYPT_MODE, pubKey) }
.doFinal(codeWord.toByteArray())
return Base64.encodeToString(encryptCodeWord, Base64.NO_WRAP)
}
catch (ex:Exception){
Crash.recordException(ex)
Crash.setKey("error_get_encrypt_code_word",ex.message)
}
return codeWord
}
and for RSA/ECB/OAEPWithSHA-256AndMGF1Padding
private fun getEncryptCodeWord(publicKey:String,codeWord:String):String{
try{
val publicBytes = Base64.decode(publicKey, Base64.NO_WRAP)
val keySpec = X509EncodedKeySpec(publicBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val pubKey = keyFactory.generatePublic(keySpec)
val sp = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT)
val encrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
encrypt.init(Cipher.ENCRYPT_MODE, pubKey, sp)
val encryptCodeWord = encrypt.doFinal(codeWord.toByteArray())
return Base64.encodeToString(encryptCodeWord, Base64.NO_WRAP)
}
catch (ex:Exception){
Crash.recordException(ex)
Crash.setKey("error_get_encrypt_code_word",ex.message)
}
return codeWord
}

Related

Decrypt message in swift encrypted in java using AES/GCM/NoPadding

I want to create secretKey(symmetricKey) using my EC privateKey and publicKey from backend(Java)
and decrypt message using that secretKey. How to achieve this? Here is implementation:
This is how backend creates sharedKey:
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
return keyAgreement.generateSecret("AES");
Here is how iOS creates sharedSecret->SymmetricKey not sure if this is right because comparing secretKeys with backend my key is different (by definition of ECDH should be same)
let serverPubKey = try P256.KeyAgreement.PublicKey(pemRepresentation: serverPublicKeyPEM)
let shared = try privateKey.sharedSecretFromKeyAgreement(with: serverPubKey)
let symetricKey = shared.hkdfDerivedSymmetricKey(using: SHA256.self,
salt: "".data(using: .utf8)!,
sharedInfo: Data(),
outputByteCount: 32)
here is how backend encrypts
byte[] plain = Base64.getEncoder().encodeToString(plainString.getBytes(StandardCharsets.UTF_8)).getBytes();
SecretKey key = generateSharedSecret(decodePrivateKey(sessionKey), decodePublicKey( devicePublicKey));
Cipher encryptor = Cipher.getInstance("AES/CTR/NoPadding", BouncyCastleProvider.PROVIDER_NAME);
IvParameterSpec ivSpec = new IvParameterSpec(INITIALIZATION_VECTOR);
encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
return Base64.getEncoder().encodeToString(encryptor.doFinal(plain, 0, plain.length));
and here is how iOS trying to decrypt
guard let payloadData = Data(base64Encoded: payload) else { return }
do {
//I know that sealedBox is wrong, should be just box? I'm not sure of this step
let sealedBox = try AES.GCM.SealedBox(combined: payloadData)
let decrypted = try AES.GCM.open(sealedBox, using: symmetricKey)
//here I'm getting authenticationFailure
} catch {
print("error: \(error)")
}

RSA Encryption Java/Kotlin

I been trying to encrypt a simple string in Kotlin/Java with a premade public key but I've had no success.
This is what I'm currently doing and commented is what I've currently tried.
val toEncrypt = "8uUrfe4OcJVUT5lkAP07WKrlGhIlAAwTRwAksBztVaa0hHdZp50EFjOmhrAmFsLQ"
val publicKeyRaw =
"-----BEGIN PUBLIC KEY-----\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJ\n" +
"hCm2Hjl00QGkxeO12Py+jytTNYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDg\n" +
"GvBhlwrWQXfI9LdA2M3xbr/4wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYY\n" +
"yGK2Fsnu67FKWjgVvQIDAQAB\n" +
"-----END PUBLIC KEY-----"
val reader = PemReader(StringReader(publicKeyRaw))
val pemObject = reader.readPemObject()
val keyBytes: ByteArray = pemObject.content
val keySpec: EncodedKeySpec = X509EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(keySpec)
val cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.ENCRYPT_MODE, key)
val cipherData: ByteArray = cipher.doFinal(toEncrypt.toByteArray())
val encryptedData = Base64.encodeToString(cipherData, Base64.DEFAULT)
Log.e("TAG", "encryptedData: $encryptedData")
Here is the code I've already tried:
/*
val publicKey = publicKeyRaw.replace("\n", "")
.replace("\\n", "")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
*/
/*
val pemParser = PEMParser(StringReader(publicKeyRaw))
val pemKeyPair : PEMKeyPair = pemParser.readObject() as PEMKeyPair
val key = JcaPEMKeyConverter().getPublicKey(pemKeyPair.publicKeyInfo)
*/
/*
val keyFactory = KeyFactory.getInstance("RSA")
val keyBytes: ByteArray = Base64.decode(publicKey.toByteArray(), Base64.DEFAULT)
val spec = X509EncodedKeySpec(keyBytes)
val fileGeneratedPublicKey = keyFactory.generatePublic(spec)
val rsaPub: RSAPublicKey = fileGeneratedPublicKey as RSAPublicKey
val publicKeyModulus: BigInteger = rsaPub.modulus
val publicKeyExponent: BigInteger = rsaPub.publicExponent
val keyFactoryAlt = KeyFactory.getInstance("RSA")
val pubKeySpec = RSAPublicKeySpec(publicKeyModulus, publicKeyExponent)
val key = keyFactoryAlt.generatePublic(pubKeySpec) as RSAPublicKey
*/
/*
val reader = PemReader(StringReader(publicKeyRaw))
val pemObject = reader.readPemObject()
val keyBytes: ByteArray = pemObject.content
val keySpec: EncodedKeySpec = X509EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(keySpec)
*/
/*
val keyFactory = KeyFactory.getInstance("RSA")
val keyBytes: ByteArray = Base64.decode(publicKey.toByteArray(), Base64.DEFAULT)
val spec = X509EncodedKeySpec(keyBytes)
val fileGeneratedPublicKey = keyFactory.generatePublic(spec)
val rsaPub: RSAPublicKey = fileGeneratedPublicKey as RSAPublicKey
val publicKeyModulus: BigInteger = rsaPub.modulus
val publicKeyExponent: BigInteger = rsaPub.publicExponent
*/
/*
val pemParser = PEMParser(StringReader(publicKey))
val pemKeyPair : PEMKeyPair = pemParser.readObject() as PEMKeyPair
val encoded : ByteArray = pemKeyPair.publicKeyInfo.encoded
val keyFactory = KeyFactory.getInstance("RSA")
val key = keyFactory.generatePublic(PKCS8EncodedKeySpec(encoded))
*/
and it actually generates a String but when using tools like: https://8gwifi.org/rsafunctions.jsp
it shows an error that it's not valid, even tough I generated the key there with a 1024 key size
My question is: How to cypher with that kind of key in Java/Kotlin. (you may generate that kind of key on any site you like or the site provided)
here is a pair I used:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJ
hCm2Hjl00QGkxeO12Py+jytTNYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDg
GvBhlwrWQXfI9LdA2M3xbr/4wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYY
yGK2Fsnu67FKWjgVvQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCAW4WQxF2/qzqYwoQlwkkQIjQJhCm2Hjl00QGkxeO12Py+jytT
NYAopHCPpR4SbhE1cFdYx1qjEnFbgeJBxFENyqDgGvBhlwrWQXfI9LdA2M3xbr/4
wur7ph1c+aQxOpImzslCtHJ5df7cyFrOTnkY+XYYyGK2Fsnu67FKWjgVvQIDAQAB
AoGActBq8wmTSiVh7s7f4d6d+D6ACZscrHjwsBtcuwUAIOONgO8TtASBNNmSjgsG
kTm/TuvEVfdMjd2rZE0UE/wE+2BOoHTlkVjcKMxoM8KbP/4RBDYlYmWTseiS8zmp
dGwchOzsoWKnhZtnvMrki0f1SdMq4J6g9RncFIrUSKWJ1MECQQDp0s4v+sKo423X
2YSAhB8j1LMPoRlioXSmvVrHGINzGoHt2tRvGqqHaHbd/9QkkhpfeeBcdrv/xOaH
fVH08dnJAkEAjIgFRe6QEDNvm1qCRx6ata047N188MxdHgKHwQBsv48dxqljQrFS
N1yEfXsv6PjLk3DCD8Wi3FTOgftpZVeWVQJBAIpc+TABJkGEW1KYX8Ug6cBtNAxy
my/3NK0abeZUxixNqkcS8BRS5kg8c+KIaYO+hSasWyy8AiGm5XeVm/LjTqkCQEGQ
dGVcF/p3BOsGHyHvNV7tolFgRJpTvl3x8EQrXpFAxDObc6P59tG9aFLi1kdrTA9N
3DxfiMwjBPW/xjxx0MECQBtaSSfTNUYBP64+evjY4HaV9GI5AK83webyF73axXIq
4dyadIdIo78Yaz+f2myX7vyfUlU5iM8QuPMN2KCM3CE=
-----END RSA PRIVATE KEY-----
here is the code I used: https://github.com/Raykud/TestEncryption
Edit: This is the generated encrypted text.
NO_WRAP: c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQxMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xKqbovZP8eoZrvp2azFOk=
DEFAULT:
c6nQMEFIrOWsPjB6W00DC6+5xaKm8R79bu8xLz9+yYhDTDepkiQGh0fWpyJuldNJit5CyL9n73TQ
xMjmtqsZsR/sAGEFjk7EGj8etwFO4MKpZY55BX1MsOVbWbfo2x31uCb/Ssd6nJnu897yCD5Md7xK
qbovZP8eoZrvp2azFOk=
The cause of your problem is that different paddings are used.
The posted ciphertext can be reproduced (with the posted public key) or decrypted (with the posted private key) if no padding is applied (RSA/ECB/NoPadding, see here). This RSA variant is called textbook RSA and shouldn't be used in practice because it's insecure. The website applies PKCS#1 v1.5 padding (the first three options) or OAEP (the last three options), the insecure textbook RSA is not supported at all. I.e. the paddings are incompatible and decryption therefore fails.
There are two ways to specify the encryption with Cipher#getInstance, the full variant algorithm/mode/padding or the short variant algorithm, see here. In the latter, mode and padding are determined by provider-specific default values. And because they are provider specific, they can be different in different environments, which can lead to cross-platform problems, as in this case. That is why the full variant should always be used!
Cipher#getInstance("RSA") obviously applies textbook RSA in your environment, i.e. no padding. I can reproduce this behavior e.g. in Android Studio (API level 28). In contrast, in Eclipse (Kotlin plugin 0.8.14) PKCS#1 v1.5 padding is used.
So the solution to the problem is to explicitly specify the padding according to the environment used, e.g. for PKCS#1 v1.5 padding usually with RSA/ECB/PKCS1Padding or RSA/NONE/PKCS1Padding, see here. Note that the scheme algorithm/mode/padding is used for both symmetric and asymmetric encryption. While the mode of operation is defined for symmetric encryption, it's generally not defined for asymmetric encryptionsuch as RSA, i.e. ECB has no meaning in the context of RSA, but is still used by some providers on the specification.
Another possible problem is that the website can't handle line breaks, but it doesn't remove them automatically, so decryption fails if the ciphertext contains line breaks. The option Base64.DEFAULT generates line breaks after 76 characters. These must therefore be removed (e.g. manually) before the ciphertext is decrypted using the website. Alternatively, Base64.NO_WRAP can be used, which produces the ciphertext on a single line.

Using public private key combination for symmetric encryption with Java 8

I'm trying to use an asymmetric private and public key combination to generate a symmetric key for encrypting and decrypting some text, but, I'm stuck unable to use the generated key as it is 128bytes in size and this is unacceptable for the AES encryption. I'd like to solve this problem using just the JRE (no external libraries). Do you have a solution?
I've included my example code below, there's a comment indicating the line I get the exception thrown.
(encryptCipher.init(Cipher.ENCRYPT_MODE, tomSecretKeySpec, iv);)
I read about KDF hashing, but Java doesn't seem to have an obvious way of invoking this on my 128byte key. Also, Im not sure this is the right answer since my understanding is that the longer the key, the more secure the encryption (for a given algorithm). Perhaps I need to switch from using AES/CBC/PKCS5Padding, but none of the other algorithms included with the JDK as standard seem to support the 128byte key either.
public void demoSymmetricEncryption() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {
String keyAlgorithm = "DiffieHellman";
String keyAgreementAlgorithm = "DiffieHellman";
String keySpecAlgorithm = "AES";
String cipherAlgorithm = "AES/CBC/PKCS5Padding";
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
keyGenerator.initialize(1024, new SecureRandom());
KeyPair tomKeyPair = keyGenerator.generateKeyPair();
PrivateKey tomPrivateKey = tomKeyPair.getPrivate();
PublicKey tomPublicKey = tomKeyPair.getPublic();
KeyPair steveKeyPair = keyGenerator.generateKeyPair();
PrivateKey stevePrivateKey = steveKeyPair.getPrivate();
PublicKey stevePublicKey = steveKeyPair.getPublic();
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("Limited encryption policy files installed : " + (maxKeyLen == 128)); // returns false
KeyAgreement tomKeyAgreement = KeyAgreement.getInstance(keyAgreementAlgorithm);
keyGenerator.initialize(1024, new SecureRandom());
tomKeyAgreement.init(tomPrivateKey);
tomKeyAgreement.doPhase(stevePublicKey, true);
byte[] tomSecret = tomKeyAgreement.generateSecret();
SecretKeySpec tomSecretKeySpec = new SecretKeySpec(tomSecret, keySpecAlgorithm);
KeyAgreement steveKeyAgreement = KeyAgreement.getInstance(keyAgreementAlgorithm);
steveKeyAgreement.init(stevePrivateKey);
steveKeyAgreement.doPhase(tomPublicKey, true);
byte[] steveSecret = steveKeyAgreement.generateSecret();
SecretKeySpec steveSecretKeySpec = new SecretKeySpec(steveSecret, keySpecAlgorithm);
System.out.println("Secret Keys are identical : " + steveSecretKeySpec.equals(tomSecretKeySpec)); // returns true
String initVector = "RandomInitVector";
Cipher encryptCipher = Cipher.getInstance(cipherAlgorithm);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
// fails because AES key is 128 bytes not 128 bits in length - think I need to use KDF hash to shrink it appropriately.
encryptCipher.init(Cipher.ENCRYPT_MODE, tomSecretKeySpec, iv);
// Attempt to use the cipher
byte[] encryptedData = encryptCipher.doFinal("Hello".getBytes());
Cipher decryptCipher = Cipher.getInstance(cipherAlgorithm);
iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
decryptCipher.init(Cipher.DECRYPT_MODE, steveSecretKeySpec, iv);
byte[] decryptedData = decryptCipher.doFinal(encryptedData);
System.out.println("Decrypted Data : " + new String(decryptedData));
}
The output from the program is as follows:
Limited encryption policy files installed : false
Secret Keys are identical : true
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 128 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:582)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:339)
at javax.crypto.Cipher.implInit(Cipher.java:806)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at crypto.SymetricEncryptionTest.demoSymmetricEncryption(SymetricEncryptionTest.java:76)
at crypto.SymetricEncryptionTest.main(SymetricEncryptionTest.java:29)
The error is: * Invalid AES key length: 128 bytes*
Valid AES key sizes are 128-bits, 192-bits and 256-bits or in bytes: 16-bytes, 24-bytes and 32-bytes.
Use an AES key size that is valid.
The general method of generation a symmetric key is just to get the bytes from a cryptographic PRNG. For Java see Class SecureRandom.
For key derivation use PBKDF2, see Class SecretKeyFactory and Java Cryptography Architecture Standard Algorithm Name Documentation "PBKDF2WithHmacSHA1" (Constructs secret keys using the Password-Based Key Derivation Function function).
For an example see OWASP Hashing Java but use "PBKDF2WithHmacSHA1" as the algorithm.
The reason the code wasn't working was that I was using incompatible algorithms. The corrections are as follows:
Replace lines:
String keyAlgorithm = "DiffieHellman";
String keyAgreementAlgorithm = "DiffieHellman";
with
String keyAlgorithm = "EC";
String keyAgreementAlgorithm = "ECDH";
int keySize = 128;
and replace lines
keyGenerator.initialize(1024, new SecureRandom());
with
keyGenerator.initialize(keySize, new SecureRandom());
Program now produces output:
Limited encryption policy files installed : false
Secret Keys are identical : true
Decrypted Data : Hello
Technically, you probably also want to Base64 encode the encrypted output and then decode it again prior to the decode as below:
String encryptedData = Base64.encode(encryptCipher.doFinal("Hello".getBytes()));
byte[] decryptedData = decryptCipher.doFinal(Base64.decode(encryptedData));

RSA encryption in Android and Java

I would like to encrypt a String with RSA encryption. My public/private keys were generated and stored in DB. In android, I use this code:
public static String encryptRSAToString(String text, String strPublicKey) {
byte[] cipherText = null;
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(strPublicKey.trim().getBytes(), Base64.DEFAULT));
Key publicKey = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
strEncryInfoData = new String(Base64.encode(cipherText,Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}
For debug purpose, I try to call 2 times this method with the same parameters and String result were similar (as expected).
I want to generate the same encrypted String in java. However, "android.util.Base64" class is not available in Java, so I've tried with the default Base64 class:
public static String encryptRSAToString(String text, String strPublicKey) {
byte[] cipherText = null;
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(strPublicKey.trim().getBytes()));
Key publicKey = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
strEncryInfoData = new String(Base64.encodeBase64(cipherText));
} catch (Exception e) {
e.printStackTrace();
}
return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}
But the String generated in Android and the one in java are different.
Generated in Android side :
Ky2T4j1JdI081ZESVJgxZXEf/xmtpehfv/EwpVvKQxUu1JI8lwXP2Rc66jHZRc0P846ZYuF3C9YEmWoKbXGXk2MBuT5KVxa2yoTbwZlMmhVOX3X3Efq0VyaO5zZ4qavIq036cA3MzvQbUAb678UdbALW/CjRCsOdeH+hSCzNQ+0=
Generated in JAVA side :
XhSLxfiJUUdZW5kWh0MEPSrqoROBBhNC/krfTx+sdnXML3WegYbMzSvNnPgB8+8Z9joEUBMmoeBI1OhTF6qPFL1EEixkFYAkGaryEFxvN/aFI75kEUj71OHNzAHAuvS+h+9Nssx9psSZ5gc2OoLQH0QtbGDyXB4p+qUGFCde4tY=
Does someone know how to solve my issue ?
thank you
It looks like you've been undone by relying on defaults. Never do that if you hope for interoperability.
Here are the two examples of mistakenly relying on defaults in your code that I've found.
final Cipher cipher = Cipher.getInstance("RSA");
The tranformation string is supposed to be of the form "algorithm/mode/padding" but you've left off the mode and padding specifications. As a result you got default values for those. The defaults are evidently different on Android and Oracle Java. You should always fully specify the transformation, for example:
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
Another bad example is
cipherText = cipher.doFinal(text.getBytes());
In text.getBytes() you are relying on the no-args getBytes() method which uses the default charset for the platform. But this default charset differs on different platforms, and thus this is not portable. In almost all cases I've run across you should specify the UTF-8 charset. So the correct line would thus be
cipherText = cipher.doFinal(text.getBytes("UTF-8"));
and the correct string constructor to use to recreate the original string in the decrypt method is the String(byte [] data, String charsetName).
I canĀ“t comment yet so I answer.
It is possible that different default configurations are being used. Check this question: Is there any difference between Apache's Base64.encodeBase64 and Android's Base64.encode with Base64.Default flag?
There are deviations of different cipher and hash implementations. I would suggest using OpenSSL as a common implementation.

AES KeyPairGenerator Not Recognised

I have an issue with my java code. I'm trying to encrypt a file. However, when I run my java code I get "java.security.InvalidKeyException: Invalid AES key length: 162 bytes".
Here is the code:
byte[] rawFile;
File f = new File("./src/wonkybox.stl");
FileInputStream fileReader = new FileInputStream(f);
rawFile = new byte[(int)f.length()];
fileReader.read(rawFile);
/***** Encrypt the file (CAN DO THIS ONCE!) ***********/
//Generate the public/private keys
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
keyGen.initialize(1024, random);
KeyPair key = keyGen.generateKeyPair();
PrivateKey privKey = key.getPrivate();
PublicKey pubKey = key.getPublic();
//Store the keys
byte[] pkey = pubKey.getEncoded();
FileOutputStream keyfos = new FileOutputStream("./CloudStore/keys/pubkey");
keyfos.write(pkey);
keyfos.close();
pkey = privKey.getEncoded();
keyfos = new FileOutputStream("./CloudStore/keys/privkey");
keyfos.write(pkey);
keyfos.close();
//Read public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("AES");
FileInputStream keyfis = new FileInputStream("./CloudStore/keys/pubkey");
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
PublicKey pub1Key = keyFactory.generatePublic(pubKeySpec);
keyfis = new FileInputStream("./CloudStore/keys/privkey");
encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
PrivateKey priv1key = keyFactory.generatePrivate(privKeySpec);
//Encrypt file using public key
Cipher cipher = Cipher.getInstance("AES");
System.out.println("provider= " + cipher.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pub1Key);
byte[] encryptedFile;
encryptedFile = cipher.doFinal(rawFile);
//Write encrypted file to 'CloudStore' folder
FileOutputStream fileEncryptOutput = new FileOutputStream(new File("./CloudStore/encrypted.txt"));
fileEncryptOutput.write(encryptedFile);
fileEncryptOutput.close();
The error occurs at the line "KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");".
AES is a symmetric algorithm, hence they use of KeyPairGenerator is not supported. To generate a key with AES you call KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); //set keysize, can be 128, 192, and 256
By looking at the rest of your code, it looks like you are trying to achive asymmetric encryption (since you call getPublic() and getPrivate() etc), so I advice you to switch to using RSA or any other asymmetric algorithm that java supports. You will most likley only need to replace AES with RSA in your getInstance(); calls, and pherhaps some fine-tuning. Good luck
As far as I know, AES is symmetric encryption algorithm i.e. it needs only one key for encryption/decryption.
From the JavaDoc of java.security.KeyPairGenerator:
The KeyPairGenerator class is used to generate pairs of public and private keys.
Meaning that it should be used for asymmetric encryption algorithms. For symmetric encryption algorithms one should use javax.crypto.KeyGenerator.
However, I advise simply mimicking some tutorial on how to encrypt / decrypt byte array in Java using AES like this one.
It uses sun.misc.Base64Encoder / Base64Decoder classes to encode / decode byte array to / from String, however you may skip this step.
Hope this helps
How can you use a keypair generator for AES? AES is a symmetric key algorithm. Refer this link. That means if you encrypt data using a key "k", then you will have to decrypt it also using the same key "k". But when you generate key pair, as the name suggests, two keys are generated and if you encrypt using one of the keys, you can decrypt only using the other key. This is the base for PKI.
If you want to use keypair generator use an algorithm like "rsa" or "dsa" in the getInstance() method like this :
KeyPairGenerator keygen=KeyPairGenerator.getInstance("rsa");
I think your code should now work fine after making the above change.

Categories