I followed much of this post with the objective to implement aes 256 encryption in my software and it works just fine
The key point here is that the whole implementation described in the above link uses the AESEngine class. Looking at the class code and javadoc reference, the AESEngine is a 128bit instead of a 256 bit block cipher
Searching trough the code and docs i could not find the 192 or 256 bits implementations. Where are them?
For completeness, this is the core of my actual ciphering class:
private void init(String passphrase) {
try {
String algorithm = "PBEWithSHA256And256BitAES-CBC-BC";
encryptCipher = createCipher();
decryptCipher = createCipher();
randomGenerator = new RandomGenerator();
PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(), KEY_SALT, ITERATIONS);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
key = keyFactory.generateSecret(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException occured while trying to generate the crypto key. This error should never occur, check the application code", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("InvalidKeySpecException occured while trying to generate the crypto key. This error should never occur, check the application code", e);
}
}
private BufferedBlockCipher createCipher() {
return new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()), new PKCS7Padding());
}
public byte[] encrypt(byte[] data) {
if (data == null)
throw new NullPointerException("Cannot encrypt null data");
byte[] iv = randomGenerator.generateRandom(IV_SIZE);
byte[] encrypted;
synchronized (encryptCipher) {
encrypted = runCipher(encryptCipher, true, data, iv);
}
return DataUtil.append(iv, encrypted);
}
public byte[] decrypt(byte[] data) {
if (data == null)
throw new NullPointerException("Cannot decrypt null data");
byte[] iv = DataUtil.extract(data, 0, IV_SIZE);
byte[] cipherText = DataUtil.extract(data, IV_SIZE, data.length - IV_SIZE);
byte[] decrypted;
synchronized (decryptCipher) {
decrypted = runCipher(decryptCipher, false, cipherText, iv);
}
return decrypted;
}
private byte[] runCipher(BufferedBlockCipher cipher, boolean forEncryption, byte[] data, byte[] iv) {
String operation = forEncryption ? "encrypt" : "decrypt";
try {
KeyParameter keyParam = new KeyParameter(key.getEncoded());
ParametersWithIV cipherParams = new ParametersWithIV(keyParam, iv);
cipher.init(forEncryption, cipherParams);
byte[] result = new byte[cipher.getOutputSize(data.length)];
int len = cipher.processBytes(data, 0, data.length, result, 0);
len += cipher.doFinal(result, len);
//Remove padding se estiver decriptografando
if(!forEncryption)
result = DataUtil.extract(result, 0, len);
return result;
} catch (DataLengthException e) {
throw new RuntimeException("DataLengthException occured while trying to " + operation + " data with length " + data.length + ". This error should never occur, check the application code", e);
} catch (IllegalStateException e) {
throw new RuntimeException("IllegalStateException occured while trying to " + operation + " data with length " + data.length + ". This error should never occur, check the application code", e);
} catch (InvalidCipherTextException e) {
throw new IllegalArgumentException("InvalidCipherTextException occured while trying to " + operation + " data with length " + data.length, e);
}
}
If you want to do AES like encryption with a block size of 256 bit you should use:
http://www.docjar.org/docs/api/org/bouncycastle/crypto/engines/RijndaelEngine.html
But that's probably not what you want; the 256 in AES-256 is about the key size. This key size is then used by the underlying 128 bit AES block cipher. AES is the standardized, 128 bit block version of Rijndael.
AES supports 3 key sizes - Wikipedia, NIST.
You're probably referring the block size, which is fixed at 128 bits.
Also, I tried going through code, it is written assuming different key sizes - 128, 192, and 256. Copy - paste from code - "AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits. This code is written assuming those are the only possible values"
Related
I'm trying to get some encryption/decryption going using AES/CBC/PKCS5Padding and am getting a strange result. Depending on the original value I use to encrypt I get an exception:
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
To test this out, I wrote a little function that starts with a string and progressively makes it bigger, trying to encrypt the string and decrypt the encrypted result in each iteration.
First iteration ==> string == "5" Encrypt and decrypt
Next iteration ==> string == "55" Encrypt and decrypt
Next iteration ==> string == "555" Encrypt and decrypt
Next iteration ==> string == "5555" Encrypt and decrypt
Next iteration ==> string == "55555" Encrypt and decrypt
If consistently fails to decrypt the encrypted values in items 0 and 4 (first and last). It successfully decrypts the other values.
Any clues what may be causing this?
Here is the output of the program:
0 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [5]
This is the ciphertext encrypted [ÂZ??¢?»NÔå?Ó^Ç ]
Encrypted Value = [C25A863FA23FBB4ED4E53FD35E7FC7A0]
DECRYPT Key: [00000000000000000000000000000000] value: [C25A863FA23FBB4ED4E53FD35E7FC7A0]
This is the ciphertext [[B#5fdef03a]
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:977)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1058)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:855)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)
at com.mgl.siebel.crypt.AES256Crypt.decrypt(AES256Crypt.java:35)
at com.mgl.siebel.crypt.AES256Crypt.test2(AES256Crypt.java:85)
at com.mgl.siebel.crypt.AES256Crypt.main(AES256Crypt.java:101)
Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
1 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [55]
This is the ciphertext encrypted []*çü×z%?eÑ¥zx~÷]
Encrypted Value = [5DAD2AE7FCD77A259665D1A57A787EF7]
DECRYPT Key: [00000000000000000000000000000000] value: [5DAD2AE7FCD77A259665D1A57A787EF7]
This is the ciphertext [[B#5ccd43c2]
Decrypted Value = [55]
2 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [555]
This is the ciphertext encrypted [M÷o?gI¶àeØÖ8c.+]
Encrypted Value = [4DF76F916749B6E065D807D638632E2B]
DECRYPT Key: [00000000000000000000000000000000] value: [4DF76F916749B6E065D807D638632E2B]
This is the ciphertext [[B#4aa8f0b4]
Decrypted Value = [555]
3 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [5555]
This is the ciphertext encrypted [ÖFè7tÔ·ðGÂ?WÂGs ]
Encrypted Value = [D646E83774D4B7F047C28657C24773A0]
DECRYPT Key: [00000000000000000000000000000000] value: [D646E83774D4B7F047C28657C24773A0]
This is the ciphertext [[B#7960847b]
Decrypted Value = [5555]
4 **************************************
ENCRYPT Key: [00000000000000000000000000000000] value: [55555]
This is the ciphertext encrypted [ȱiã?'èÀ0<eäy?]
Encrypted Value = [C80EB169E33F27E8C0AD303C65E4791B]
DECRYPT Key: [00000000000000000000000000000000] value: [C80EB169E33F27E8C0AD303C65E4791B]
This is the ciphertext [[B#2aae9190]
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:977)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1058)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:855)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)
at com.mgl.siebel.crypt.AES256Crypt.decrypt(AES256Crypt.java:35)
at com.mgl.siebel.crypt.AES256Crypt.test2(AES256Crypt.java:85)
at com.mgl.siebel.crypt.AES256Crypt.main(AES256Crypt.java:101)
Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
Here is the code being executed
public String decrypt(String key, String encryptedRawValue) throws Exception {
System.out.println("DECRYPT Key: [" + key + "] value: [" + encryptedRawValue + "]");
try {
if ((key == null) || (encryptedRawValue == null)) {
throw new Exception("key and value must not be null");
}
// convert raw value into its original encrypted sequence of bytes
byte[] ciphertext = DatatypeConverter.parseHexBinary(encryptedRawValue);
System.out.println("This is the ciphertext [" + ciphertext + "]");
byte[] raw = key.getBytes(Charset.forName("UTF-8"));
if (raw.length != 32) {
throw new IllegalArgumentException("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] original = cipher.doFinal(ciphertext);
String plainTextValue = new String(original, Charset.forName("UTF-8"));
return (plainTextValue);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public String encrypt(String key, String value) throws Exception {
System.out.println("ENCRYPT Key: [" + key + "] value: [" + value + "]");
try {
byte[] raw = key.getBytes(Charset.forName("UTF-8"));
if (raw.length != 32) {
throw new Exception("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
String encryptedValue = new String(cipher.doFinal(value.getBytes(Charset.forName("UTF-8"))));
System.out.println("This is the ciphertext encrypted [" + encryptedValue + "]");
String rawValue = DatatypeConverter.printHexBinary(encryptedValue.getBytes());
return (rawValue);
} catch(Exception e) {
e.printStackTrace();
throw e;
}
}
private void test2() throws Exception {
String key = "00000000000000000000000000000000";
try {
String value = "";
for (int i=0; i < 5; i++) { // loop 5 times encrypting and decrypting
System.out.println("\n" + i + " **************************************\n");
try {
value = value + "5";
String encryptedValue = this.encrypt(key, value);
System.out.println("Encrypted Value = ["+ encryptedValue + "]");
String plainTextValue = this.decrypt(key, encryptedValue);
System.out.println("Decrypted Value = ["+ plainTextValue + "]");
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
AES256Crypt c = new AES256Crypt();
try {
c.test2();
} catch(Exception e) {
e.printStackTrace();
}
}
}
String encryptedRawValue
This cannot work. Strings are a sequence of characters. Encrypted data is a sequence of bytes. If we live in magic unicornland where unicode, and more generally western characters, can just be waved away as being non-existent, you can write really bad code and conflate the two. This was common in ye olden days. it's so bad, it's a major reason for why python 2 decided to up and ditch it all and move to python 3.
There is only one fix. Stop doing this. The correct type is byte[]. If you then need this byte[] rendered in string form for some reason, then the only sane reason is because it needs to be rendered in a highly limited venue, such as an email. In which case, you should base64 encode it. Search the web for 'java base64' on how to do this, if you must. Those APIs get it right: The 'encode' method takes a byte[] and returns a String, and the decode method takes a String and returns a byte[].
Fix this issue and the problem goes away.
String encryptedValue = new String(cipher.doFinal(value.getBytes(Charset.forName("UTF-8"))));
Do not wrap the result of doFinal into new String. Take the byte array. That's your data.
This can be a duplicate of Exception: "Given final block not properly padded" in Linux, but it works in Windows but not entirely.
My application works in windows, but fails in Linux with
Given final block not properly padded exception
Configuration:
JDK Version: 1.8u162
Windows : version 10
Linux : OpenSuSE 42.2 (x86_64)
My code is below:
public static Cipher createCipher(int mode, String passPhrase) throws CypherException {
// Salt value
byte[] salt = new byte[128]; // Should be atleast 8 bytes
SecureRandom secRandom = new SecureRandom(passPhrase.getBytes(StandardCharsets.UTF_8));
secRandom.nextBytes(salt); // Self-seeded randomizer for salt
// Iteration count
int iterationCount = 12288;
int derivedKeyLength = 256 ; // Should be atleast longer than 112 bits. Depends on Key size of algorithm.
Cipher cipher;
try {
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount, derivedKeyLength * 8);
SecretKey key = SecretKeyFactory.getInstance(APBEWithHmacSHA512AndAES_256).generateSecret(keySpec);
byte iv[] = new byte[16];
secRandom.nextBytes(iv); // Self-seeded randomizer to generate IV
IvParameterSpec ivSpec = new IvParameterSpec(iv) ; // IvParameterSpec initialized using its own randomizer
// Note: there is no typical transformation string. Algorithm, mode (CBC) and padding scheme (PKCS5Padding) is all taken care by ALGORITHM_NAME.
cipher = Cipher.getInstance(PBEWithHmacSHA512AndAES_256);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
// Create the Cipher
cipher.init(mode, key, paramSpec);
} catch (NoSuchPaddingException nspe) {
throw new CypherException("No such Padding: " + nspe.getMessage());
} catch (NoSuchAlgorithmException nsae) {
throw new CypherException("Algorithm not supported: " + nsae.getMessage());
} catch (InvalidKeyException ike) {
throw new CypherException("Invalid key: " + ike.getMessage());
} catch (InvalidKeySpecException ikse) {
throw new CypherException("Invalid key specification: " + ikse.getMessage());
} catch (InvalidAlgorithmParameterException iape) {
throw new CypherException("Invalid algorithm parameter: " + iape.getMessage());
}
return cipher;
}
public static void decrypt(InputStream in, OutputStream out, String passPhrase) throws IOException, CypherException {
// Bytes read from in will be decrypted
in = new CipherInputStream(in, createCipher(Cipher.DECRYPT_MODE, passPhrase));
int numRead;
// Buffer used to transport the bytes from one stream to another
byte[] buf = new byte[1024];
// Read in the decrypted bytes and write the cleartext to out
try {
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
} finally {
// close the streams
}
}
And I am getting
javax.crypto.BadPaddingException: Given final block not properly
padded
at following line:
while ((numRead = in.read(buf)) >= 0) {
Now, as suggested in Exception: "Given final block not properly padded" in Linux, but it works in Windows by Maarten Bodewes I am already using PBKDF2 as PBEWith*, really is the PBKDF2 + encryption scheme (CBC mode with PKCS5Padding). So why the encryption done on Windows doesn't work on Linux in my case? Can someone help please?
The following code worked perfectly on Android 5,
now on Android 6 I have this assert error:
junit.framework.ComparisonFailure: expected:
This is clear te[xt right now]
but was:
This is clear te[]
at testAndroidAesCfbDecrypther(AesCfbCryptherTest.java:112)
This function works on Motorola Moto G Android 5.1, Samsunsg S5 Android 5.1 and emulator with Android 5.1. It doesn't work on Motorola Moto G Android 6 and emulator with Android 6.
public void testAndroidAesCfbDecrypther() {
Cipher AESCipher;
final String password = "th3ke1of16b1t3s0"; //password
final byte[] IV = Hex.toBytes("aabbccddeeff3a1224420b1d06174748"); //vector
final String expected = "This is clear text right now";
final byte[] encrypted1 = Hex.toBytes("a1ea8e1c4d8579b84e3e8d48d17fe916a70079b1bdc75841667cc15f");
final byte[] encrypted2 = Hex.toBytes("73052b25306059dda5d6880aa873383124448a38bcb3a769f6aed2f5");
try {
byte[] key = password.getBytes("US-ASCII");
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec IVSpec = new IvParameterSpec(IV);
AESCipher = Cipher.getInstance("AES/CFB/NoPadding"); //Tried also with and without "BC" provider
AESCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, IVSpec);
byte[] dec1 = AESCipher.update(encrypted1);
String r = new String(dec1);
assertEquals(expected, r); //assert fail here
byte[] dec2 = AESCipher.update(encrypted2);
r = new String(dec2);
assertEquals(expected, r);
} catch (NoSuchAlgorithmException e) {
...
}
}
For testing purposes i tried also with 'doFinal', but second assertion fails:
ByteArrayOutputStream bytesStream1 = new ByteArrayOutputStream();
byte[] dec1 = AESCipher.update(encrypted1);
bytesStream1.write(dec1);
byte[] dec2 = AESCipher.doFinal();
bytesStream1.write(dec2);
r = new String(bytesStream1.toByteArray());
assertEquals(expected, r); //ASSERTION OKAY
ByteArrayOutputStream bytesStream2 = new ByteArrayOutputStream();
dec1 = AESCipher.update(encrypted2);
bytesStream2.write(dec1);
dec2 = AESCipher.doFinal();
bytesStream2.write(dec2);
r = new String(bytesStream2.toByteArray());
assertEquals(expected, r); //ASSERTION FAIL
Just as a test I tried the same thing in ruby and it works:
require 'openssl'
expected = "This is clear text right now"
encrypted1 = ["a1ea8e1c4d8579b84e3e8d48d17fe916a70079b1bdc75841667cc15f"].pack('H*')
encrypted2 = ["73052b25306059dda5d6880aa873383124448a38bcb3a769f6aed2f5"].pack('H*')
decipher = OpenSSL::Cipher.new('AES-128-CFB')
decipher.decrypt
decipher.key = "th3ke1of16b1t3s0" #password
decipher.iv = ["aabbccddeeff3a1224420b1d06174748"].pack('H*') #vector
puts "TEST1-------------------"
puts (decipher.update(encrypted1) + decipher.final) == expected ? "OK" : "FAIL"
puts "------------------------"
puts "TEST2-------------------"
puts (decipher.update(encrypted2) + decipher.final) == expected ? "OK" : "FAIL"
puts "------------------------"
Block ciphers have many different modes of operation. Some like CBC require an additional padding, because only multiples of the block size can be encrypted, but others like CFB are streaming modes without padding.
If you use padding, then the contract is that full blocks are returned from Cipher#update, but the last block that must be padded or unpadded, can only be returned from Cipher#doFinal.
Since CFB mode doesn't need padding, it really shouldn't have this restriction, but then you would have changed the contract, because now Cipher#update can return incomplete data. If this contract is to be enforced even for CFB mode, then the implementation will be consistent and possibly even easier (because of intermediate values and the shift register of CFB).
You really need to finish the decryption and combine the output yourself. It's easy to do this with a ByteArrayOutputStream, but you can also use three System.arraycopy calls.
ByteArrayOutputStream fullPlaintextStream = new ByteArrayOutputStream();
byte[] dec1 = AESCipher.update(encrypted1);
fullPlaintextStream.write(dec1);
byte[] dec2 = AESCipher.update(encrypted2);
fullPlaintextStream.write(dec2);
byte[] dec3 = AESCipher.doFinal();
fullPlaintextStream.write(dec3);
r = new String(fullPlaintextStream.toByteArray());
assertEquals(expected, r);
Discrepancy between Android 5.1 and 6.0 because of changes in providers
Android has multiple JCE providers for different algorithms. In this specific case, there was overlap between the BouncyCastle provider ("BC") and AndroidOpenSSL provider, because both of them supported AES-CFB at the same time, but AndroidOpenSSL was higher up in the provider list, so it took precendence. See for yourself with this:
for(Provider p : Security.getProviders()) {
System.out.println("Provider " + p.getName());
for(Map.Entry e : p.entrySet()) {
System.out.println(" " + e.getKey() + " : " + e.getValue());
}
}
Finally, CFB was removed for Android 6.0 (corresponding commit). Compare the providers for 5.1.1 and 6.0.1. So in Android 6 only the BouncyCastle provider supports CFB mode, which works in the same way as described in the first part of this answer.
Possible solutions:
Replace the provider in Android 6 with an older version of conscrypt (the one from Android 5).
CFB is a streaming mode, so this fact can be used to write a wrapper around the Cipher class for CFB to always return the same amount of output bytes as were passed in. The idea is to fill up incomplete blocks with 0x00 bytes and XOR the corresponding output bytes with the first bytes of the next update call to produce some output.
Tried to run the code in java - jdk 1.6 but it fails.
Below is what I tried, if its of any help - (modified to be able to run in eclipse by default) :
public static void testAndroidAesCfbDecrypther() {
Cipher AESCipher;
final String password = "th3ke1of16b1t3s0"; //password
final byte[] IV = DatatypeConverter.parseHexBinary("aabbccddeeff3a1224420b1d06174748"); //vector
final String expected = "This is clear text right now";
final byte[] encrypted1 = DatatypeConverter.parseHexBinary("a1ea8e1c4d8579b84e3e8d48d17fe916a70079b1bdc75841667cc15f");
final byte[] encrypted2 = DatatypeConverter.parseHexBinary("73052b25306059dda5d6880aa873383124448a38bcb3a769f6aed2f5");
try {
byte[] key = password.getBytes("US-ASCII");
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec IVSpec = new IvParameterSpec(IV);
AESCipher = Cipher.getInstance("AES/CFB/NoPadding"); //Tried also with and without "BC" provider
AESCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, IVSpec);
byte[] dec1 = AESCipher.update(encrypted1);
String r = new String(dec1);
assertEquals(expected, r); //assert fail here
byte[] dec2 = AESCipher.update(encrypted2);
r = new String(dec2);
assertEquals(expected, r);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
private static void assertEquals(String left, String right) {
System.out.println(left+":"+right);
System.out.println(left.equals(right));
}
Output :
This is clear text right now:This is clear te
false
This is clear text right now:xt right nowThis is clear text r
false
May be the default buffer sizes changed.
Can you run the above in the two emulators and post the same ?
Also below code helps in identifying the CipherSpi implementation used (assuming security manager does not complain) :
private static void printCipherDetails(Cipher cipher) {
try {
for(Field field : cipher.getClass().getDeclaredFields() ){
field.setAccessible(true);
if( field.getType() == javax.crypto.CipherSpi.class ) {
Object object = field.get(cipher);
System.out.print("Name :"+field.getName()+". ");
if( object != null ) {
System.out.println("CipherSpi :"+object.getClass());
}
else {
System.out.println("CipherSpi not initialized!");
}
}
else if( field.getType() == java.security.Provider.class ) {
Object object = field.get(cipher);
System.out.print("Name :"+field.getName()+". ");
if( object != null ) {
System.out.println("Provider :"+object.getClass());
}
else {
System.out.println("Provider not initialized!");
}
}
}
}catch (Exception e) {
e.printStackTrace();
}
System.out.println("");
}
When invoked after cipher.init(), prints details such as noted below :
Name :b. Provider :class com.sun.crypto.provider.SunJCE
Name :c. CipherSpi :class com.sun.crypto.provider.AESCipher
Name :j. CipherSpi not initialized!
I am converting my C# encryption code to Android.
I am facing issue like I am not able to encrypt the text as same as C#.
Below I copy paste both code.
Both are working code regarding using it you can use any password & any plain text .You will find both have different output.
C# CODE
System.security.Cryptography.RijndaelManaged AES = new System.Security.Cryptography.RijndaelManaged();
System.Security.Cryptography.MD5CryptoServiceProvider Hash_AES = new System.Security.Cryptography.MD5CryptoServiceProvider();
final MessageDigest Hash_AES = MessageDigest.getInstance("MD5");
String encrypted = "";
try {
byte[] hash = new byte[32];
byte[] temp = Hash_AES.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(pass));
final byte[] temp = Hash_AES.digest(pass.getBytes("US-ASCII"));
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
AES.Key = hash;
AES.Mode = System.Security.Cryptography.CipherMode.ECB;
System.Security.Cryptography.ICryptoTransform DESEncrypter = AES.CreateEncryptor();
byte[] Buffer = System.Text.ASCIIEncoding.ASCII.GetBytes(input);
encrypted = Convert.ToBase64String(DESEncrypter.TransformFinalBlock(Buffer, 0, Buffer.Length));
} catch (Exception ex) {
}
return encrypted;
Here is my Android java code.
ANDROID JAVA CODE
private static String TRANSFORMATION = "AES/ECB/NoPadding";
private static String ALGORITHM = "AES";
private static String DIGEST = "MD5";
byte[] encryptedData;
public RijndaelCrypt(String password,String plainText) {
try {
//Encode digest
MessageDigest digest;
digest = MessageDigest.getInstance(DIGEST);
_password = new SecretKeySpec(digest.digest(password.getBytes()), ALGORITHM);
//Initialize objects
_cipher = Cipher.getInstance(TRANSFORMATION);
_cipher.init(Cipher.ENCRYPT_MODE, _password);
encryptedData = _cipher.doFinal(text);
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key (invalid encoding, wrong length, uninitialized, etc).", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
Log.e(TAG, "Invalid or inappropriate algorithm parameters for " + ALGORITHM, e);
return null;
} catch (IllegalBlockSizeException e) {
Log.e(TAG, "The length of data provided to a block cipher is incorrect", e);
return null;
} catch (BadPaddingException e) {
Log.e(TAG, "The input data but the data is not padded properly.", e);
return null;
}
return Base64.encodeToString(encryptedData,Base64.DEFAULT);
}
Should I need to use "US-ASCII" in pass or does it take it?
Use the same mode of operation: either ECB or CBC
Use the same character set: it's best to stick to "UTF-8"
Use the same key: in the C# code you're doubling the 128-bit key to 256 bits
When using CBC with a random IV, it is expected that the ciphertext differs for the same plaintext. The decryption is the operation that determines whether you succeeded.
Note that ECB is not semantically secure. Use CBC with a random IV. The IV doesn't have to be secret, so you can just prepend it to the ciphertext and slice it off before decryption.
It's better to use an authenticated mode like GCM or EAX or if it's not provided an encrypt-then-MAC scheme. It's hard to implement it correctly yourself so stick to some library that does this for you like RNCryptor.
I have an AES 128-bit encryption method which encrypts messages. But it does not encrypt messages which are longer than 45 characters. Therefore, i was thinking if i can do that by splitting messages with longer than 45 chars into an array of size 45 chars or array list, then i encrypt each block at a time.
So, if i have a message of long 90, i should to have two arrays of size 45 each (or array of size two and each of size 45chars). or an arraylist to do that. if i have a message of long 180 then i should have a block of 4 arrays.
UPDATE: So my initial issue was in not being able to send long messages using AES encryption in a client-server chat.
The following are the codes of AES implementation but without any padding or IV in it.
First i have a client; who sends messages encrypted using AESmsgEncryption class below.
Client class
public void send(String sendMessage) {
userName = GUI.getUserName();
//each index of the array will contain a substring of the send message (maximum length 45 char)
if (sendMessage.length() > 45) {
// String [] arrayOfStrings = new String[sendMessage.length()/45];
int sizeOfArray = sendMessage.length()/45;
System.out.println("CANT SEND MESSAGES LONGER THAN 45");
} else {
String ciphertext = msgAESEnDe.encryptAES(sendMessage, scrtkey);
System.out.println(userName + ": " + ciphertext + " <-- encrypted before sending");
out.println(userName + "#ciphertext$" + ciphertext);
out.flush();
}
}
Once the client sends out a message, the receiver then receives it at it is(encrypted) and sends it back to the online clients. Once the online clients receives the encrypted text message they decrypt the message using the following code In another receive method in the client class
String username = recMessage.substring(0, recMessage.indexOf(' '));
String ciphertxtWithNoName = recMessage.substring(recMessage.indexOf(':') + 2);
scrtkey = new SecretKeySpec(secrtKeyByte, "AES");
String plaintext = msgAESEnDe.decryptedPlain(ciphertxtWithNoName, scrtkey);
System.out.println(username +" " + plaintext +" <-- after decryption");
//current time
String time = log.format(new Date());
// Displaying received message.
ClientGUI.TA_CONVERSATION.append(time+ " ["+username.substring(0, username.length()-1) + "]: " + plaintext + "\n");
So in AESmsgEncryption class i have a method as follows;
public final String encryptAES(final String plaintext, SecretKey key) {
String ciphertext = new String();
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] bytePlaintext = plaintext.getBytes();
byte[] byteCiphertext = cipher.doFinal(bytePlaintext);
ciphertext = new BASE64Encoder().encode(byteCiphertext);
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException: " + e);
} catch (NoSuchPaddingException e) {
System.out.println("NoSuchPaddingException: " + e);
} catch (InvalidKeyException e) {
e.printStackTrace();
System.out.println("InvalidKeyException: " + e);
} catch (IllegalBlockSizeException e) {
System.out.println("IllegalBlockSizeException: " + e);
} catch (BadPaddingException e) {
System.out.println("BadPaddingException: " + e);
}
return ciphertext;
}
public final String decryptedPlain(String ciphertext, SecretKey key) {
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte [] decodedValue = new Base64().decode(ciphertext.getBytes());
plaintext = cipher.doFinal(decodedValue);
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException: " + e);
} catch (NoSuchPaddingException e) {
System.out.println("NoSuchPaddingException: " + e);
} catch (InvalidKeyException e) {
System.out.println("InvalidKeyException: " + e);
} catch (IllegalBlockSizeException e) {
System.out.println("IllegalBlockSizeException: " + e);
} catch (BadPaddingException e) {
System.out.println("BadPaddingException: " + e);
}
return new String(plaintext);
}
The AES secret key is generated in the server side and sent encrypted using RSA, and then sent to each client, that is why in the client i have to give the scrtkey in the calling to the encryptAES and decryptedPlain methods.
To re state my problem; the above code cannot send messages longer than 45 chars, how can i make it send messages longer than 45 chars? If i needed to use padding and IV, the server should send the IV along with the secret key, right?
I hope the above description is clear.
How can i do that in JAVA?
Thanks
These kind of padding that you are getting is normally not related to any limitation of the cipher. It's because:
you loose data during communication;
you loose data during encoding/decoding.
The last one is most likely, and if you use a random IV or key, it's likely that it happens randomly. In that case splitting the string may only temporarily hide the issue instead of solving it.
If the ciphertext is handled as string data you should use base 64 encoding. Make sure that the full ciphertext in bytes is the same after the encrypt method and before the decrypt method by viewing it as hexadecimal string.
AES can easily encrypt gigabytes of information (although the security depends on the use case and mode of operation). Splitting the plaintext should not be necessary.
I think you could something like this:
int length=input.length();
for(int i=0; i<length; i+=45) {
encrypt(input.substring(i, i+45<=length ? i+45 : length ));
}
A bit of explanation about i+45<=length ? i+45 : length
The format is expression ? if yes : if no
It checks if i+45 <= length
When true: Substring from i to i+45
When false: Substring from i to the end (length)
If you wouldn't do this check you will get an error of type StringIndexOutOfBoundsException. (Unless your input is exactly 45, 90, 135 etc bytes long)
More info: here