I have to change java code for decrypt to flutter code. I use library "encrypt" for flutter. my flutter code show
Key length not 128/192/256 bits.
my java spring boot code.
public static String decryptAES256(String str, String secretKey) throws java.io.UnsupportedEncodingException,
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String AES = "AES";
String AES_PADDING = "AES/CBC/PKCS5Padding";
byte[] keyData = java.util.Base64.getDecoder().decode(secretKey);
String iv = secretKey.substring(0, 16);
SecretKey secureKey = new SecretKeySpec(keyData, AES);
Cipher c = Cipher.getInstance(AES_PADDING);
c.init(Cipher.DECRYPT_MODE, secureKey, new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));
byte[] byteStr = Base64.getDecoder().decode(str.getBytes());
return new String(c.doFinal(byteStr), StandardCharsets.UTF_8);
}
my flutter code.
final encData = codefModel.data!.encData!;
final key = en.Key.fromUtf8(decryptKey);
final b64key = en.Key.fromUtf8(base64Url.encode(key.bytes));
final iv = en.IV.fromLength(16);
final encrypter = en.Encrypter(en.AES(b64key, mode: en.AESMode.cbc));
logger.d(encrypter.decrypt64(encData, iv: iv));
From the Java code it can be deduced that the AES key secretKey and the ciphertext str are Base64 encoded. The first 16 bytes of the Base64 encoded key (not of the raw key!) are used as IV.
In the Dart code the key is incorrectly converted and a wrong IV is used. A possible fix is (together with sample data):
const decryptKey = "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=";
const encData = "lo0dOoJrHNRuefPXgkEa6jtDUhV5CguF9MHWTJ4Y8eGP8zHaKJEQIuOTZxstyk3X";
final key = en.Key.fromBase64(decryptKey);
final iv = en.IV(Uint8List.fromList(utf8.encode(decryptKey).sublist(0, 16)));
final encrypter = en.Encrypter(en.AES(key, mode: en.AESMode.cbc));
final decrypted = encrypter.decrypt64(encData, iv: iv); // The quick brown fox jumps over the lazy dog
As test, key and ciphertext can be used in the Java code that returns the same plaintext for them.
Be aware that deriving the IV from the key is generally insecure, since encryptions with the same key automatically lead to the reuse of key/IV pairs. Instead, a random IV should be used for each encryption.
Related
I have been trying to send data effectively between two applications. One is implemented in C# (Sender) the other in Java (Receiver). The sender has to encrypt data using the transformation "AES/GCM/NOPadding" with a 32-byte key while the receiver has to decrypt using the same parameters. Here is the sender encryption function in C# (using bouncy castle)
public static string Encrypt(string plainText, string msgId)
{
const byte GcmTagSize = 16;
byte[] hashKey;
byte[] secretKey = Encoding.UTF8.GetBytes(msgId);
Console.WriteLine(secretKey.Length);
using (var hasher = SHA512.Create())
{
byte[] digestSeed = hasher.ComputeHash(secretKey);
hashKey = new byte[16];
Array.Copy(digestSeed, hashKey, hashKey.Length);
}
var keyParameter = new KeyParameter(hashKey);
var keyParameters = new AeadParameters(keyParameter, GcmTagSize * 8, secretKey);
var cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding");
cipher.Init(true, keyParameters);
var plainTextData = Encoding.ASCII.GetBytes(plainText);
var cipherText = cipher.DoFinal(plainTextData); //bouncy castle
return Convert.ToBase64String(cipherText);
}
Here is the receiver decryption function in java
private static byte[] decrypt(byte[] message, SecretKey key) throws NoSuchPaddingException, NoSuchAlgorithmException,
IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
Cipher cipher = Cipher.getInstance("AES/GCM/NOPadding"); //NoPadding
byte[] nonce = Arrays.copyOfRange(message, message.length - cipher.getBlockSize(), message.length);
byte[] encryptedKycData = Arrays.copyOf(message, message.length - cipher.getBlockSize());
System.out.println(doEncode(nonce));
System.out.println(doEncode(nonce));
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, nonce);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
byte[] decryptedValue=cipher.doFinal(encryptedKycData);
return decryptedValue;
}
When I try to decrypt with the java decryption function I get the error
javax.crypto.AEADBadTagException: Tag mismatch
Struggling with this issue and any help would be appreciated.
I wrote a simple Encryption and Decryption helper class for my android app to encrypt and store Strings securely.
It consists of a single static public method to encrypt, then it calls a private static method to decrypt the encrypted message and returns it. I wrote the method this way to check if the message is intact after encryption/decryption.
I wrote a simple JUnit test with a String and called AssertEquals on the String before and after sending it to the Crypto encryption method.
I get this following errors from running the test:
javax.crypto.AEADBadTagException: Tag mismatch!
The error stack:
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:571)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1046)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:983)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at util.Crypto.decrypt(Crypto.java:94)
at util.Crypto.encrypt(Crypto.java:64)
at com.example.ali.meappley.CryptoTest.encryptAndDecryptTest(CryptoTest.java:29)
I'm new to cryptography, but I read different stackoverflow replies and couldn't find anything of help. Some users suggested calling cipher.update(someByteArray) before calling cipher.doFinal(someByteArray) but I couldnt manage to get it working. Any suggestions?
This is my helper class
public class Crypto {
//public methods
//public static encrypt method
public static String encrypt(String messageToEncrypt, #Nullable byte[] associatedData) throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
byte[] plainBytes = messageToEncrypt.getBytes();
/////////////////////////////////////////////////////////////////
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
byte[] cipherText = cipher.doFinal(plainBytes);
ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + cipherText.length);
byteBuffer.putInt(iv.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
Arrays.fill(key,(byte) 0); //overwrite the content of key with zeros
///////////////////////////////////////////////////////////////////
byte[] decrypted = decrypt(cipherMessage, null, key);
return decrypted.toString();
}
//public static decrypt method
private static byte[] decrypt(byte[] cipherMessage, #Nullable byte[] associatedData, byte[] key) throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
ByteBuffer byteBuffer = ByteBuffer.wrap(cipherMessage);
int ivLength = byteBuffer.getInt();
if(ivLength < 12 || ivLength >= 16) { // check input parameter
throw new IllegalArgumentException("invalid iv length");
}
byte[] iv = new byte[ivLength];
byteBuffer.get(iv);
byte[] cipherText = new byte[byteBuffer.remaining()];
byteBuffer.get(cipherText);
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
cipher.update(cipherText);
byte[] plainText= cipher.doFinal(cipherText);
return plainText;
}
There are a few issues with your code:
1) In your encrypt-method remove the following line (or shift it behind the decrypt-call).
Arrays.fill(key, (byte) 0); // overwrite the content of key with zeros
Otherwise the key for encryption and decryption differ.
2) In your encrypt-method also pass the associatedData in your decrypt-call i.e. replace
byte[] decrypted = decrypt(cipherMessage, null, key);
with
byte[] decrypted = decrypt(cipherMessage, associatedData, key);
The associatedData passed for encryption and decryption have to match for validity. For the purpose of the associatedData see e.g. https://crypto.stackexchange.com/questions/6711/how-to-use-gcm-mode-and-associated-data-properly
3) In your decrypt-method remove the line
cipher.update(cipherText);
For the purpose of the update-method see e.g. What does cipher.update do in java?
All three issues give rise to an AEADBadTagException.
4) I suspect for testing purposes your encrypt-method returns decrypted.toString() which however only gives you the object's class and hashcode. It would make more sense to return e.g. new String(decrypted).
I am trying to decrypt a String with a known key in Java using standard Cipher API.
The encrypted String comes from a Web Service using the standard CommonCrypto Library which responds with some statistics as encrypted strings at regular intervals.
The specs are AES/CBC/PKCS7Padding with KeySize = 32 Bytes and BlockSize = 16 Bytes, and Encoding UTF-8 (raw) & Base64. I intend to write a Java client that can request these statistics, decrypt them and store them for later analyses.
Question 1. Does the CommonCrypto automatically pad keys with extra chars if the key is short? For instance less than 16 Bytes or 32 Bytes.
Question 2. What encoding measures should I take to ensure an identical encryption/decryption on both ends?
Example Strings and Key
String message = "mQp9sp8ri1E0V1Xfso1d5g==Mrf3wtaqUjASlZmUO+BI8MrWsrZSC0MxxMocswfYnqSn/VKB9luv6E8887eCxpLNNAOMB0YXv6OS7rFDFdlvC53pCHo3cVZiLJFqgWN/eNiC9p4RMxyFCcOzWrwKzT5P8sy55DwE25DNJkvMthSaxK5zcP1OdLgBiZFOSxYRsX4rBk7VP7p5xr2uTGjRL+jmGgB9u3TmeCNCr8NxGLNt6g==";
String userKey = "123456789";
private static String decrypt (String message, String userKey) throws UnsupportedEncodingException,
NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
ShortBufferException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchProviderException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
if (message.length() >= 48) {
ivFromEncryptedString = message.substring(0, Math.min(message.length(), 24));
messageFromEncryptedString = message.substring(24, message.length());
System.out.println(ivFromEncryptedString);
System.out.println(messageFromEncryptedString);
byte[] data = decodeBase64(messageFromEncryptedString);
byte[] ivData = decodeBase64(ivFromEncryptedString);
paddedKey = padShortKeys(userKey);
byte[] keyBytes = paddedKey.getBytes(CHARSET);
MessageDigest sha = MessageDigest.getInstance("SHA-256"); //key
keyBytes = sha.digest(keyBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
byte [] encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
} catch (Exception e) {
System.out.println(e);
} finally {
return encrypted;
}
}
return null;
}
private static String encodeBase64(byte [] in){
return Base64.getEncoder().encodeToString(in);
}
private static byte[] decodeBase64(String str) throws UnsupportedEncodingException {
return DatatypeConverter.parseBase64Binary(str);
}
Also with the current code status I am getting placehoder characters instead of the desired result.
Thanks in advance folks. :)
CommonCrypto is unclear, which implementation are you using? Apple, Apache, Java Class Cipher or another, please supply a link to it.
Never assume an encryption will pad the key or IV, they should always be provided in the exact length, there is no standard for such padding. If they need padding (they shouldn't) do it yourself.
Typically if encrypted data needs to be expressed as a character string Base64 encoding is used.
As James states, for one-shot encryption just use doFinal(ByteBuffer input, ByteBuffer output) which
encrypts or decrypts data in a single-part operation.
Note: A 9 digit key only has about 33-bits of security which is not close to sufficient. Simple using a hash function is insufficient for deriving an encryption key from a password, instead PBKDF2 or Argon2 should be used.
I am trying to encrypt and decrypt using AES/CBC/NoPadding in JAVA. I did the encryption in both JAVA and PHP using (mcrypt) and got the same result, using the same key and iv. However, when I try to decrypt in JAVA, I get the word correctly but always with extra characters. I read other questions and found that I need to add padding. So I added Padding5 but got the same result. Anyways, I need it without padding because that is how it works in PHP. Any help is appreciated. My code is below and the result is here:]2
public class RijndaelCrypt {
//private String key = "2a4e2471c77344b3bf1de28ab9aa492a444abc1379c3824e3162664a2c2b811d";
private static String iv = "beadfacebadc0fee";
private static String hashedKey = "6a2dad9f75b87f5bdd365c9de0b9c842";
private static Cipher cipher;
public static String decrypt(String text) throws UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
SecretKeySpec keyspec = new SecretKeySpec(hashedKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decodedValue = Base64.decode(text.getBytes("UTF-8"));
byte[] decryptedVal = cipher.doFinal(decodedValue);
return new String(decryptedVal);
}
public static String encryptNew(String data) throws Exception {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes("UTF-8");
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(hashedKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("UTF-8"));
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return DatatypeConverter.printBase64Binary(encrypted);
}
public static void main (String [] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
String data = "Hello";
System.out.println("New Decrypted: " + RijndaelCrypt.decrypt(RijndaelCrypt.encryptNew(data)));
System.out.println("New Encryption: " + RijndaelCrypt.encryptNew(data));
}
}
The PHP mcrypt wrapper (or underlying mcrypt library) pads with zero bytes up to the block length (zero to 15 padding bytes, if 16 is the block size of the cipher). After that the blocks are encrypted by the cipher.
When decrypting in Java you need to manually remove any zero bytes from the right hand side of the plaintext after decryption using NoPadding. The zero valued padding bytes can of course be seen when hex-encoding the decrypted plaintext. However when outputting a string the zero bytes are either left out or converted to a replacement character (depending on the character set and terminal).
Note that the PHP zero padding has one big drawback: if the plaintext ends with one or more zero valued bytes it could be stripped from the decrypted plaintext by any unpadding routine. This is why PKCS#7 padding (which pads 1 to 16 bytes) should be preferred.
Also note that PHP actually needs rtrim("\0") to remove the zero bytes itself; mcrypt just leaves them there, but they generally won't be printed.
Note that Bouncy Castle crypto libraries also has ZeroPadding as option. However, this is zero padding of 1 to 16 bytes (i.e. it always pads/unpads) so it is incompatible with the padding defined used by PHP mcrypt and may fail if the size of the plaintext can be divided by the block size of the cipher.
I am using CryptoJS to encrypt at the client side I have created a fiddle of the client side code as following -
var onClick = function() {
var iv = "3ad5485e60a4fecde36fa49ff63817dc";
var key = "0a948a068f5d4d8b9cc45df90b58d382d2b916c25822b6f74ea96fe6823132f4";
var encrypted = CryptoJS.AES.encrypt("{'This is ' : 'A Nice day'}",
key, {
iv : CryptoJS.enc.Hex.parse(iv),
mode : CryptoJS.mode.CBC,
padding : CryptoJS.pad.Pkcs7
});
var encryptedInHex = encrypted.ciphertext
.toString(CryptoJS.enc.Hex); // converting the encrypted data in Hexadecimal
document.getElementById("thisDiv").innerHTML = encryptedInHex.toUpperCase();
return encryptedInHex.toUpperCase(); // returning the hashed encrypted data.
};
I have also developed a fiddle for it here -
http://jsfiddle.net/akki166786/1c24d1mj/3/
This is symmetric key cryptography being used here.
when I am trying to decrypt it on server side I am getting,
javax.Crypto.BadPaddingException : Given final block not properly padded exception,
Can there be a problem from Client side as well?
I need a server side(written in java) code to decrypt the out put of the function which i Have written in fiddle.
Thanks for your help.
You'll find the Java code to decrypt the ciphertext below. A couple of notes first:
server-side code uses commons-codec for hex decoder
in client-side code, replace key parameter to encrypt function with CryptoJS.enc.Hex.parse(key)
That should be it. And now the code ...
public class Decryptor {
private static final String IV = "3ad5485e60a4fecde36fa49ff63817dc";
private static final String KEY = "0a948a068f5d4d8b9cc45df90b58d382d2b916c25822b6f74ea96fe6823132f4";
private final static String CIPHERTEXT = "4E6B9EC6B5A0614BF9D69C5B0B5AE03D27484F2DB9D450E50EE623E80B8E34F5";
public static final void main(String[] args) throws DecoderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
SecretKeySpec sks = new SecretKeySpec(Hex.decodeHex(KEY.toCharArray()), "AES");
IvParameterSpec iv = new IvParameterSpec(Hex.decodeHex(IV.toCharArray()));
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, sks, iv);
byte[] plain = c.doFinal(Hex.decodeHex(CIPHERTEXT.toCharArray()));
String plainText = new String(plain);
System.out.println(plainText);
}
}