I'm trying to change encryption algorithm of existing project. But i have a little bit confusion. When i use "PBEWithHmacSHA512AndAES_256" as a parameter, it produce different result but when i use "PBEWithMD5AndDES" as a parameter it produce same result. My functions are :
public static synchronized String encrypt1(final String textToEncrypt, final String pathPublicKey) throws Exception {
final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(pbeKeySpec);
// Prepare the parameter to the ciphers
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());
// Create the ciphers
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
// Encode the string into bytes using utf-8
final byte[] utf8 = textToEncrypt.getBytes("UTF8");
// Encrypt
final byte[] enc = cipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
}
public static synchronized String encrypt2 (final String textToEncrypt, final String pathPublicKey) throws Exception {
final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithHmacSHA512AndAES_256").generateSecret(pbeKeySpec);
// Prepare the parameter to the ciphers
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());
// Create the ciphers
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
// Encode the string into bytes using utf-8
final byte[] utf8 = textToEncrypt.getBytes("UTF8");
// Encrypt
final byte[] enc = cipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
}
Any suggestions, ideas will help me to figure out what's going on here.
Also this is produce different results:
KeyStore keyStore = KeyStore.getInstance("JCEKS");
keyStore.load(new FileInputStream((pathOfJKSfile)), password.toCharArray());
Key key = keyStore.getKey(keyName, keyPass.toCharArray());
byte[] raw = key.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "PBEWithHmacSHA512AndAES_256");
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);
final Cipher cipherEncrypt = Cipher.getInstance(ALGORITHM);
cipherEncrypt.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
final byte[] enc = cipherEncrypt.doFinal(messageBytes);
System.out.println( new sun.misc.BASE64Encoder().encode(enc));
And i know that cipher.init() using "JceSecurity.RANDOM" for pruducing different results.
Both algorithms, PBEWithHmacSHA512AndAES_256 and PBEWithMD5AndDES, first generate an encryption key by processing a password, a salt and an iteration count (using HmacSHA512 and MD5, respectively) and then encrypt the plain text (with AES-256 and DES, respectively) using this key and the CBC-mode. When the Cipher-instance is initialized, a pseudo-random initialization vector (IV) is generated that is required for the CBC- mode.
In the context of PBEWithHmacSHA512AndAES_256, the IV is generated using the SecureRandom implementation of the highest-priority installed provider, at least for the Cipher#init()-method used in the code (note that there are several overloads of the Cipher#init()-method and that a SecureRandom-instance can also be passed explicitly). I.e. with each Cipher-initialization a new (random) IV is generated and therefore the encrypted text is always different, even for an identical plain text. For this reason, the encrypted text in your examples changes in this context.
In the context of PBEWithMD5AndDES, the IV is only determined by the password, the salt, the iteration count (and of course the MD5-hash-algorithm itself). Therefore, the IV and the encrypted text do not change in case of repetition (provided that password, salt, iteration count etc. are the same). For this reason, the encrypted text in your example does not change in this context.
The generation of a new, random IV during the Cipher-initalization makes sense with regard to the following requirements for the IV: For security reasons, an IV in CBC-mode (btw this also applies to other modes) may only be used once under the same key. In addition the IV must be unpredictable.
PBEWithMD5AndDES is deprecated.
EDIT:
The use of an IV is standard nowadays (for security reasons). A lot of information can be found on the Internet on this topic e.g. here. In the following I will only describe a few basic things.
The IV used for encryption must be stored in some way because it is required for decryption. The IV does not have to be kept secret, so that it is usually concatenated with the encrypted data (e.g. before the encrypted data) and stored together with them. During decryption, both parts can be separated because the length of the IV is known (16 Byte for AES). E.g for the concatenation in the encryption-method something like the following is used (let iv and enc be the byte-arrays with the IV and the encrypted data, respectively):
byte[] result = new byte[enc.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(enc, 0, result, iv.length, enc.length);
and in the decryption-method the corresponding counterpart (having in mind that the length of an IV in AES is 16 Byte).
In the encryption-method the IV can be determined with Cipher#getIV() (this must of course happen after calling Cipher#init()).
In the decryption-method you have to pass the IV to the PBEParameterSpec-ctor (e.g. let iv be the byte-array with the IV):
IvParameterSpec ivSpec = new IvParameterSpec(iv);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
The generation of an IV can also take place outside the Cipher-class, see e.g. Generating random IV for AES in Java. Then you have to pass that IV in the encryption-method in the same way as above described for the decryption-method.
Note, in connection with an IV some points have to be considered e.g. using a mode without an IV (e.g. ECB), using an IV consisting exclusively of 0-values, using a predictable IV or using an IV more than once under the same key etc. drastically reduces security in general, see e.g. here!
Related
Currently, I'm having a problem that I don't know how to solve. It's the decryption and encryption of the string using AES256. Everything was working fine until I restarted the server and I couldn't decode the previous data.
I tried saving the salt and IVParameter to decrypt for next time, but it doesn't work.
private static final String SECRET_KEY = "my_key";
private static final byte[] SALT;
private static final SecureRandom random;
private static final IvParameterSpec ivspec;
static {
random = new SecureRandom();
SALT = new byte[16];
random.nextBytes(SALT);
byte[] bytesIV = new byte[16];
random.nextBytes(bytesIV);
ivspec = new IvParameterSpec(bytesIV);
}
public static String encrypt(String stringToEncrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e);
}
return null;
}
public static String decrypt(String stringToDecrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e);
}
return null;
}
SecureRandom initialises itself differently everytime you instantiate it. I.e., it will also create a different sequence of random values each time. Even if you initialise SALT with a fixed initial value, in the next step you overwrite it again by calling random.nextBytes(SALT). Either don't do that or instantiate SecureRandom with a seed, so it creates the same sequence of random numbers every time. But this is kind of counter-productive. Similarly, you also randomise IvParameterSpec.
You only need the nextBytes() result, if you want to generate new salt or IV values for a multiple users or a sequence of distinct encryption/decryption actions. AES being a symmetric cypher, you need to make sure that when decrypting a message, you use the same salt and IV (if any) which were used for encryption. Try this in order to get identical encryption results:
static {
random = new SecureRandom(); // not used in this example
SALT = "I am so salty!".getBytes(StandardCharsets.UTF_8);
byte[] bytesIV = "my super fancy IV".getBytes(StandardCharsets.UTF_8);
ivspec = new IvParameterSpec(Arrays.copyOfRange(bytesIV, 0, 16));
}
Of course, in the example above I am assuming that actually salt and IV were initially created randomly, then securely saved or transmitted to the recipient, and then loaded/received and used to decrypt the message. In a real-world scenario, you would transmit or store salt and IV asymmetrically encrypted (using public-key cryptography), while the message itself (which usually is much bigger than secret key, salt and IV) is encrypted using the much faster and more efficient symmetric AES256 algorithm.
P.S.: The Arrays.copyOfRange(bytesIV, 0, 16) is necessary, because in contrast to the salt the IV must be exactly 16 bytes long. The salt is more flexible.
Update: Actually, it is not necessary to encrypt salt and IV. They just make sure that the same input and secret key do not yield the same encrypted message in order to make attacks based on known cleartext more difficult. This is also why e.g. when storing salted hashes in a database, you store the salt values as cleartext along with the salted password hash (not the password itself!), because you need them every time you want to validate a user password.
I use the code like 3des-encryption-decryption-in-java,but when I use decrypt it,it got a error like this
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
07-17 11:27:27.580: WARN/System.err(22432): at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:705)
07-17 11:27:27.580: WARN/System.err(22432): at javax.crypto.Cipher.doFinal(Cipher.java:1111)
But if I change final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); to final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");,the method can run but got a wrong result (the mode is different from server).
So I want to know the reason about it.
The decrypt method:
public static String decrypt(byte[] message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest(token.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
The encrypt method:
public static byte[] encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest(token
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; ) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv, new SecureRandom(new byte[5]));
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return cipherText;
}
There are many possibilitiesThe most common is if you atr encoding the key as a String, especially without specifying the character encoding. If you want to do this, use Base-64, which is designed to encode any binary data, rather than a character encodingAlso make sure that source platform and target platform encoding should be same.As you are using UTF-8 here and then on the other en , UTF-8 must be usedNow have a look under the facts which you are telling that code is running with final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding"); but not with final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
While Decryption, you must know the padding size and mode in which you have selected at encryption time.As you are saying that when you use CBC mode than it throws exception but when you changed it to CFB then it got able to run.In this case you need to make sure which mode you are using at encrytpion time.
As a side note: CBC, OFB and CFB are identical, however OFB/CFB is better because you only need encryption and not decryption, which can save code space.
CBC(Cipher Block Chaining) is used where the data goes through the AES function, and feedback is applied to modify the pre-encrypted data, so that repeated plain data does not yield the same encrypted data. Data can only be processed in blocks which match the block-size of the underlying encryption function (so 128-bit blocks on the case of AES), and synchronisation at this block level must be provided between the encrypting and decrypting engines, otherwise data will be indecipherable
CFB(Cipher FeedBack mode) is also a common mode, and offers the possibility of making an underlying block cipher work like a stream cipher; ie. so that the data being processed can be a stream of shorter values (for example bytes or even individual bits) rather than being processed only as much larger blocks.In CFB mode, the data itself does not go through the AES engine, but gets XORed with a value which the AES engine generates from previous message history. This means that the latency through the CFB function can be minimised, as the only processing applied to the data is an XOR function. Data widths can be set to any size up to the underlying cipher block size, but note that throughput decreases as the widths get smaller in the ratio of the data width to the block size.(Side note ended :D)
If you encrypt using Cipher-Feedback (CFB) or Output-Feedback (OFB) or counter (CTR) modes then the ciphertext will be the same size as the plaintext and so padding is not required. Be careful, though, when using these modes, because the initialisation vectors (IV) must be unique.
Similarly, encrypting using a stream cipher like RC4 or PC1 does not require padding.
Now if we investigate more critically than you should take care about the block size and padding size(already mentioned above).Now the first thing you need to make sure is that the padding size defined by your encryption algorithm.As I mentioned that in CFB case padding is not required so first try it without giving padding.if issue still resides then check either it is pkcs5 or pkcs7.Try your code by setting decrytpion padding size to pkcs7.If it is pkcs7 then I guess it should work with CBC too.I recommend you to read Using Padding in Encryption As a additional information
PKCS#5 padding is defined in RFC 2898 (PKCS #5: Password-Based Cryptography Specification Version 2.0).
PKCS5 padding is a padding scheme for extending arbitrary data to match the block-size of a block cipher in a way that allows the receiving end to reliably remove the padding.
PKCS#7 (CMS, RFC 3369) defines a padding scheme, but it is an extension of PKCS#5 padding for block ciphers that have more than 8 bytes for block.
I am trying to decrypt an encrypted text.
I have the salt value, iteration count and key length. But i don't have the initialization vector (IV) value, how can i go about and decrypt this. I also have secret key.
For time being I am using some random IV value whose size is 16 bytes.
But still i am not able to decrypt the value properly.
Can anyone please help as i am stuck with this for a long time?
Below are the values which were given to me.
salt= EW0h0yUcDX72WU9UiKiCwDpXsJg=, Iteration=128,Keylenght=16.
MasterKeyName="Passphrase1", MACMethod algo = hmac-sha1, MACKey="jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH 6Mlg" encrypted kae = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE" and valuemac="lbu+9OcLArnj6mS7KYOKDa4zRU0=".
Secret key = "xxxxxxxxxxx".
Below is the code which I am using to decrypt.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(secretkey.toCharArray(), salt, iterationCount, keyStrength);
SecretKey tmp = factory.generateSecret(spec);
key = new SecretKeySpec(tmp.getEncoded(), "AES");
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameters params = dcipher.getParameters();
iv = "0000000000000000".getBytes();
System.out.println("IV " + new sun.misc.BASE64Encoder().encodeBuffer(iv));
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = new sun.misc.BASE64Decoder().decodeBuffer(base64EncryptedData);
byte[] utf8 = dcipher.doFinal(decryptedData);
You cannot decrypt the first block of CBC encrypted ciphertext if you don't know the IV.
It is however not unlikely that you can retrieve the IV value:
often the IV value is 16 bytes retrieved after the key bytes) generated from the PBKDF;
the IV is often prepended to the ciphertext, resulting in one block of garbage before the full plaintext during decryption;
not secure but the IV is also left out or set to a constant value, with an all-zero IV being the most common (this is identical in CBC mode to not using any IV.)
SecureRandom random = new SecureRandom(); // quite heavy, look into a lighter method.
String stringToEncrypt = "mypassword";
byte[] realiv = new byte[16];
random.nextBytes(realiv);
Cipher ecipher = Cipher.getInstance("AES");
SecureRandom random = new SecureRandom(); // quite heavy, look into a lighter method.
byte[] realiv = new byte[16];
random.nextBytes(realiv);
byte[] secret = "somelongsecretkey".getBytes();
SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");
ecipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
byte[] encryptedData = ecipher.doFinal();
but the init() only takes in 3 parameters. I need a way to do something like:
ecipher.init(Cipher.ENCRYPT_MODE, stringToEncrypt, secretKey, random);
In general you don't need something that generates random numbers for an algorithm that has deterministic behavior. Furthermore, you don't need an IV when you are using ECB block mode, which is what Java defaults to. To be precise, Java defaults to "AES/ECB/PKCS5Padding" for in Cipher.getInstance("AES").
So you should be OK with code like this:
// lets use the actual key value instead of the platform specific character decoding
byte[] secret = Hex.decodeHex("25d6c7fe35b9979a161f2136cd13b0ff".toCharArray());
// that's fine
SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");
// SecureRandom should either be slow or be implemented in hardware
SecureRandom random = new SecureRandom();
// first create the cipher
Cipher eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// filled with 00h characters first, use Cipher instance so you can switch algorithms
byte[] realIV = new byte[eCipher.getBlockSize()];
// actually fill with random
random.nextBytes(realIV);
// MISSING: create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(realIV);
// create the cipher using the IV
eCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
// NOTE: you should really not encrypt passwords for verification
String stringToEncrypt = "mypassword";
// convert to bytes first, but don't use the platform encoding
byte[] dataToEncrypt = stringToEncrypt.getBytes(Charset.forName("UTF-8"));
// actually do the encryption using the data
byte[] encryptedData = eCipher.doFinal(dataToEncrypt);
Now that looks a whole lot better. I've used the Apache commons codec for decoding the hexadecimal string.
Note that you need to save the realIV with the encryptedData, and that you haven't included integrity protection, e.g. a MAC (for passwords, you may not need that though).
I strongly suspect that what you want to do is call ecipher.doFinal(stringToEncrypt), possibly after a series of doUpdate(...) if you have longer strings.
.init() creates the cipher object, update() and doFinal() populate the encrypted output and take the plaintext as input.
Of course, you'll need to convert between String and a byte array.
I am in the process of implementing a Java library in Ruby. I have come across the following road block. Is it possible to implement the following code in ruby? Are there any ruby equivalents for byte[], IvParameterSpec, SecretKeySpec ?
private String decrypt(String token)
{
//parse token into its IV and token components
byte[] ivAndToken = Base64.decodeBase64(token);
byte[] iv = new byte[ivLength];
System.arraycopy(ivAndToken, 0, iv, 0, ivLength);
int length = ivAndToken.length - ivLength;
byte[] tokenBytes = new byte[length];
System.arraycopy(ivAndToken, ivLength, tokenBytes, 0, length);
//prepare initialization vector specification
IvParameterSpec spec = new IvParameterSpec(iv);
//create cipher instance based on transformer params
Cipher cipher = Cipher.getInstance(algorithm + mode + padding, CRYPTO_PROVIDER);
//convert key bytes into valid key format
Key key = new SecretKeySpec(Base64.decodeBase64(symkey), algorithm);
//initialize cipher for decryption
cipher.init(Cipher.DECRYPT_MODE, key, spec);
//decrypt the payload
String plaintext = new String(cipher.doFinal(tokenBytes));
return plaintext;
}
You'll probably have to implement both IvParameterSpec and SecretKeySpec on Ruby if you want the algorithm to behave exactly like it does in Java. byte[] is of course just a byte array. You'll probably want to at the docs for them (links above) and also hopefully you understand block cipher operation modes work.
If you don't, SecretKey refers to the symmetric key (eg: the passphrase), and IV is the initialization vector, a cryptographic nonce used to make different encryptions of the same plaintext generate different ciphertext. IV's are needed for all operation modes except ECB. See this wikipedia page for more details.