I've written a an encryption/decryption prototype with Java using sample code in this answer. However, I'm trying to play with AES' counter mode (CTR) and the encrypted values appear to be just as incrementable as the integer sequence that I'm trying to encrypt.
Consider the following output of my prototype:
i = 0: enc='5941F8', dec='000', length=6
i = 1: enc='5941F9', dec='001', length=6
i = 2: enc='5941FA', dec='002', length=6
i = 3: enc='5941FB', dec='003', length=6
i = 4: enc='5941FC', dec='004', length=6
i = 5: enc='5941FD', dec='005', length=6
i = 6: enc='5941FE', dec='006', length=6
i = 7: enc='5941FF', dec='007', length=6
i = 8: enc='5941F0', dec='008', length=6
i = 9: enc='5941F1', dec='009', length=6
i = 10: enc='5940F8', dec='010', length=6
i = 11: enc='5940F9', dec='011', length=6
i = 12: enc='5940FA', dec='012', length=6
Notice how the enc values usually only differ from the dec values by a single digit. Are encrypted values generated by AES' counter mode usually this iterable/similar to each other or am I doing something wrong?
So far, I have tried playing with different encryption keys, init vectors, padding schemes, longer/shorter integer sequences (starting from different values) etc. but nothing seems to work so far. Also, Google and other SO questions on the Java AES Cipher in Counter Mode have been of little use so far. Please bear in mind also that I'm a crypto newbie.
Code for my prototype is as follows:
public class Encryptor {
public static String encrypt(String key, String initVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return DatatypeConverter.printHexBinary(encrypted);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decrypted = cipher.doFinal(DatatypeConverter.parseHexBinary(encrypted));
return new String(decrypted);
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV
System.out.println(decrypt(key, initVector,
encrypt(key, initVector, "Hello World")));
for (int i = 0; i < 1000; ++i) {
String encrypted = encrypt(key, initVector, StringUtils.leftPad("" + i, 3, '0'));
String decrypted = decrypt(key, initVector, encrypted);
int encLen = encrypted.length();
System.out.println("i = " + i + ": enc='" + encrypted + "', dec='" + decrypted + "', length=" + encLen);
}
}
}
CTR mode is a streaming mode of operation. It means that a nonce and key pair creates a unique key stream which is then XORed with the plaintext. Since, XOR is symmetric, the exact same operation is applied to decrypt the ciphertext.
Now, XOR work bit-wise. If you use the same key and nonce pair for multiple encryptions, you will encrypt the plaintext with the exact same key stream every time. This means that the bit positions that are different between two plaintext will also be different when comparing the two resulting ciphertexts. This is called the two-time pad or the many-time pad.
In your example, if you XOR enc and dec for each iteration, you will always get 0x6971C8. Those are the first three bytes of the key stream. The ASCII character 0 is 0x30 which when XORed with 0x59 is 0x69 and so on.
The solution would be to use a different nonce (number used once) every time you encrypt with the same key. The nonce (sometimes called IV) is supposed to be smaller than the block size of the underlying block cipher. AES has a block size of 16 bytes. We generally choose random nonces with a length of 8 bytes or 12 bytes depending on how much data we want to encryption without a counter collision. A 12 byte nonce seems like the best compromise, because you can generate many random nonces without much of a chance of a collision and you can encrypt up to 68 GB with a key+nonce pair without a chance of a counter collision.
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.
I get a encrypted base64 string from Python.
The format is AES 256 CBC, but when I try to decrypt using Android it return decrypted string as nil.
Python
# coding=utf-8
import base64
from random import choice
from string import letters
try:
from Crypto import Random
from Crypto.Cipher import AES
except ImportError:
import crypto
import sys
sys.modules['Crypto'] = crypto
from crypto.Cipher import AES
from crypto import Random
class AESCipher(object):
def __init__(self, key):
self.bs = 32
self.key = key
def encrypt(self, raw):
_raw = raw
raw = self._pad(raw)
print raw, ';'
print _raw, ';'
iv = "".join([choice(letters[:26]) for i in xrange(16)])
print " iv :", iv
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
a = (self.bs - len(s) % self.bs)
b = chr(self.bs - len(s) % self.bs)
return s + a * b
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
def encrypt(k, t):
o = AESCipher(k)
return o.encrypt(t)
def decrypt(k, t):
o = AESCipher(k)
return o.decrypt(t)
def main():
k = "qwertyuiopasdfghjklzxcvbnmqwerty"
s1 = "Hello World!"
d2 = encrypt(k, s1)
print " Password :", k
print "Encrypted :", d2
print " Plain :", decrypt(k, d2)
if __name__ == '__main__':
main()
Java
Here I use https://github.com/fukata/AES-256-CBC-Example
final String aEcodedSting = "aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk";
String decrypted = AESUtil.decrypt(aEcodedSting);
When I try to decrypt I got this
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}:
java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
This is the Python encryption output:
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
iv : iegejanpeybezgmy
plainText : ser456&*(
Please notify me when anyone can solve this using another library.
There are 4 problems:
Difference between python output and java input
Different IV and key
Different key creation
Padding
1) Currently your python code output is a base64 encoding of iv + encrypted_data
return base64.b64encode(iv + cipher.encrypt(raw))
But in java you're directly decrypting raw data.
You should fix this way
// Decode base64
byte[] array = Base64.decode(src);
// Get only encrypted data (removing first 16 byte, namely the IV)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Decrypt data
decrypted = new String(cipher.doFinal(encrypted));
2) You must use same IV and key for input and output, so you should copy them from python console output:
iv : qbmocwtttkttpqvv
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : anZxZHVpaWJpb2FhaWdqaCK0Un7H9J4UlXRizOJ7s8lchAWAPdH4GRf5tLAkCmm6
Plain : Hello World!
and paste in java code:
private static final String ENCRYPTION_KEY = "qwertyuiopasdfghjklzxcvbnmqwerty";
private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";
3) In python you're using the key as string, but in java library it is hashed before being used for decrypting, so you should change your makeKey() method:
static Key makeKey() {
try {
byte[] key = ENCRYPTION_KEY.getBytes("UTF-8");
return new SecretKeySpec(key, "AES");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
4) Finally, you don't need to specify a padding in java with "AES/CBC/PKCS5Padding", because this way you force Cipher to pad automatically.
You can simply use "AES/CBC/NoPadding" in your decrypt() method, so it should look like this:
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
byte[] array = Base64.decode(src);
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
Java output with your base64 and IV:
encrypted: aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
decrypted: ser456&*(
Edit:
As suggested by Artjom B. (thank you), it would be better to read IV directly from ciphertext instead of hardcoding in AESUtil.
Your input consists of the IV in first 16 bytes and encrypted text in last 16 bytes, so you could take advantage of this.
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
// Decode input
byte[] array = Base64.decode(src);
// Read first 16 bytes (IV data)
byte[] ivData = Arrays.copyOfRange(array, 0, 16);
// Read last 16 bytes (encrypted text)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded)
cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData));
// Decrypt same old way
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
Moreover, as said here
Python code uses a 32 byte block size for padding which means that Java will still not be able to decrypt half of all possible ciphertexts. AES block size is 16 bytes and this should be changed in the Python implementation
You could change your Python class as below (AES.block_size is equal to 16):
class AESCipher(object):
def __init__(self, key):
self.bs = AES.block_size
self.key = key
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'm try to be compatible Encrypt/Decrypt both C# and Java.
As I know the default mode is 'ecb/pkcs5' in Java, and 'cbc/pkcs7' in C#.
So I match these things.
1st question is that PKCS7 and PKCS5 are compatible each other??,
there is no PKCS7 in Java so I use PKCS5. but I can get same encrypted data [even the padding-way is different ,pkcs7/pkcs5,] Is it possible? or these are compatible?
2nd question is that Why I get same result even though the mode, way are all different?
I compare 'DES-ECB / DES-CBC / TripleDES-ECB' these things. and C# is working well, results are all different.
Input > HELLO Output > (ECB)/dZf3gUY150= (CBC) V17s5QLzynM= (Triple)sWGS0GMe1jE
but I get same reulst in Java ..
Input > HELLO Output > (ECB)/dZf3gUY150= (CBC)/dZf3gUY150= (Triple)/dZf3gUY150=
When debugging the flow is right.
Here is my code.
C#
public static string Encrypt_DES(string originalString, byte[] key, string mode)
{
DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
if (mode.Equals("ECB"))
cryptoProvider.Mode = CipherMode.ECB;
else if (mode.Equals("CBC"))
{
cryptoProvider.Mode = CipherMode.CBC;
cryptoProvider.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
}
cryptoProvider.Padding = PaddingMode.PKCS7;
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(key, key), CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
writer.Write(originalString);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
public static string Encrypt_TripleDES(string source, string key)
{
TripleDESCryptoServiceProvider desCryptoProvider = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashMD5Provider = new MD5CryptoServiceProvider();
byte[] byteHash;
byte[] byteBuff;
byteHash = hashMD5Provider.ComputeHash(Encoding.UTF8.GetBytes(key));
desCryptoProvider.Key = byteHash;
desCryptoProvider.Mode = CipherMode.ECB; //CBC, CFB
desCryptoProvider.Padding = PaddingMode.PKCS7;
byteBuff = Encoding.UTF8.GetBytes(source);
string encoded = Convert.ToBase64String(desCryptoProvider.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
return encoded;
}
Java(Android)
public String Encrypt(String str, String desKey, String mode) {
try {
KeySpec keySpec = null;
SecretKey key = null;
Cipher ecipher = null;
if (desKey.length() == 8) {
keySpec = new DESKeySpec(desKey.getBytes("UTF8"));
key = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
if(mode.equals(ECB)){
ecipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, key);
}else if (mode.equals(CBC)){
ecipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
ecipher.init(Cipher.ENCRYPT_MODE, key,ivSpec);
}
} else if (desKey.length() == 24) {
keySpec = new DESedeKeySpec(desKey.getBytes("UTF8"));
key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
ecipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, key);
}
byte[] data = str.getBytes("UTF-8");
byte[] crypt = ecipher.doFinal(data);
return Base64.encodeToString(crypt, 0);
} catch (Exception ex) {
Log.d("ZVM", ex.getMessage());
}
return null;
}
As I understand 'IV' is for CBC, When making password, it is mixed with IV(not the key but like key). Is it right?
Thanks.
PKCS7 and PKCS5 are compatible each other
PKCS#5 and PKCS#7 paddings are compatible (equal) for DES. For AES, Java actually uses PKCS#7 padding even though you would write AES/xyz/PKCS5Padding.
Why I get same result even though the mode, way are all different?
First, let's see how Java behaves. The ciphertexts for DES-ECB, DES-CBC and DESede-ECB are all equal. This is correct if
the key is the same (DES supports only 8 byte keys, but Triple DES supports 8, 16 and 24 byte keys where non-24 byte keys are expanded to 24 byte keys),
the plaintext is the same,
the plaintext is less than 8 bytes long (block size of DES/Triple DES) and
the IV is an all 0x00 bytes IV.
Those are all true in the Java code. If you have trouble grasping that, combine the encryption routines for the ECB and CBC modes of operation.
The result of Triple DES might be a bit confusing. I assume that you've taken your 8 byte key for DES and replicated it either twice or thrice for use in Triple DES. This is an issue, because Triple DES encryption consists of three steps of normal DES: EDE means Encryption + Decryption + Encryption. If all the three subkeys are the same, the one of the Encryption steps cancels out with the Decryption step and the whole thing is equivalent to a single DES encryption.
Let's see why C# behaves differently:
The ciphertext from DES-CBC is different from DES-ECB, because the IV is not an all 0x00 bytes IV. cryptoProvider.CreateEncryptor(key, key) creates an Encryptor with the IV set to key (the second argument). That's not what you want. Just use cryptoProvider.CreateEncryptor() instead.
The ciphertext from DESede-ECB is different from DES-ECB, because you're running the key through a hash function. The key is therefore different.
Don't use DES nowadays. It only provides 56 bit of security. AES would be a much better, because it's more secure with the lowest key size of 128 bit. There is also a practical limit on the maximum ciphertext size with DES. See Security comparison of 3DES and AES.
I got an exception in the following code for AES algorithm in java.
Code decryptes an encrypted string and returns the original string.
Plz help me to fix this.
Code:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i; for (i = 0; i < buf.length; i++)
{
if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
exception:
javax.crypto.BadPaddingException: Given final block not properly padded at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..)
According to Java™ Cryptography Architecture (JCA) Reference Guide (emphasis mine):
Cipher objects are obtained by using
one of the Cipher getInstance() static
factory methods. Here, the algorithm
name is slightly different than with
other engine classes, in that it
specifies not just an algorithm name,
but a "transformation". A
transformation is a string that
describes the operation (or set of
operations) to be performed on the
given input to produce some output. A
transformation always includes the
name of a cryptographic algorithm
(e.g., DES), and may be followed by a
mode and padding scheme.
A transformation is of the form:
"algorithm/mode/padding" or
"algorithm"
For example, the following are valid transformations:
"DES/CBC/PKCS5Padding"
"DES"
If just a transformation name is specified, the system will
determine if there is an
implementation of the requested
transformation available in the
environment, and if there is more than
one, returns there is a preferred one.
If both a transformation name and a
package provider are specified, the
system will determine if there is an
implementation of the requested
transformation in the package
requested, and throw an exception if
there is not.
If no mode or padding is specified,
provider-specific default values for
the mode and padding scheme are used.
For example, the SunJCE provider uses
ECB as the default mode, and
PKCS5Padding as the default padding
scheme for DES, DES-EDE and Blowfish
ciphers. This means that in the case
of the SunJCE provider:
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.
Using modes
such as CFB and OFB, block ciphers can
encrypt data in units smaller than the
cipher's actual block size. When
requesting such a mode, you may
optionally specify the number of bits
to be processed at a time by appending
this number to the mode name as shown
in the "DES/CFB8/NoPadding" and
"DES/OFB32/PKCS5Padding"
transformations. If no such number is
specified, a provider-specific default
is used. (For example, the SunJCE
provider uses a default of 64 bits for
DES.) Thus, block ciphers can be
turned into byte-oriented stream
ciphers by using an 8 bit mode such as
CFB8 or OFB8.
Appendix A of this document contains a
list of standard names that can be
used to specify the algorithm name,
mode, and padding scheme components of
a transformation.
The objects returned by factory
methods are uninitialized, and must be
initialized before they become usable.
Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCE and that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
WARNING: You should not use ECB mode in real code. Try CBC instead.
Update: I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16.
Have you checked the length of buf[]?
Your code manages to get almost everything wrong. Just for a start, your mistakes include:
generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
Your exception handling is incorrect.
Furthermore, your code runs without exceptions for me.