3des with 2 different keys in java getting null.
import java.security.spec.*;
import javax.crypto.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
System.out.println("firstEncryption Value : "+firstEncryption);
String decryption = desDecryption(firstEncryption, k2);
System.out.println("decryption Value : "+decryption);
String secondEncryption = desEncryption(decryption, k1);
System.out.println("secondEncryption Value : "+secondEncryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
}
keyGen.init(112); // key length 56
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] encryptedText = cipher.doFinal(strToEncrypt.getBytes());
String encryptedString =base64encoder.encode(encryptedText);
return encryptedString;
} catch (Exception ex) {
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(strToDecrypt);
byte[] plainText = cipher.doFinal(encryptedText);
String decryptedString= bytes2String(plainText);
return decryptedString;
} catch (Exception ex) {
}
return null;
}
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i <bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
}
while i'm running the above code i'm getting null values. plz help.
output:
firstEncryption Value : jAihaGgiOzBSFwBWo3gpbw==
decryption Value : null
secondEncryption Value : null
getting error:
firstEncryption Value : ygGPwCllarWvSH8td55j/w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:
294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:145)
at DESedeEncryption.main(DESedeEncryption.java:107)
decryption Value : null
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:130)
at DESedeEncryption.main(DESedeEncryption.java:109)
secondEncryption Value : null
Symmetric ciphers work by encrypting and decrypting with the same key, hence the name symmetric. (And for most modes also the same IV, but the IV doesn't need to be secret.) You're encrypting with one key and decrypting with an independent key which is different with overwhelming probability (i.e. it might the same once a zillion quillion eternities). That won't work.
Perhaps you are confused by the description of Triple-DES also known as 3DES DESede or TDEA. The original DES (or DEA) cipher uses a 56-bit key (in 8 bytes) which was secure in the 1960s but not now. Triple-DES was defined using DES as a building block but with a bundle of 3 keys (k1,k2,k3) which can also be treated as a combined 168-bit key (in 24 bytes); if k3=k1 the key is described as 112-bits although it is still stored as 24 bytes. Your call to KeyGenerator "DESede" .init(112) does exactly that; it generates a 24-byte bundle with k3=k1 and k2 different. For convenience in the past Triple-DES is defined to use single-DES to encrypt with k1, decrypt with k2, and encrypt with k3, and the reverse when decrypting, hence the name DES-EDE or DESede. See http://en.wikipedia.org/wiki/Triple_DES .
If you really want, you can implement Triple-DES yourself in Java using Cipher "DES" by doing E,D,E (or reverse D,E,D if used) and then wrapping the mode around that, see Java Triple DES encryption with 2 different keys . But it's much easier to just use Cipher "DESede", which it does the lot for you, treating DESede like any other symmetric cipher primitive, as answered in that question.
Also, mode ECB is dangerous. It is an exaggeration to say it is always insecure as some people do, but historically very many applications using it designed by non-experts are insecure. Unless you know much more than is evident in your question, or are following (or interfacing to) a design by someone who does, use a better established mode like CBC or CTR.
Related
I am trying to encode in nodejs and decryption for the same in nodejs works well. But when I try to do the decryption in Java using the same IV and secret, it doesn't behave as expected.
Here is the code snippet:
Encryption in nodeJs:
var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
_ = require('lodash'),
secret = 'd6F3231q7d1942874322a#123nab#392';
function encrypt(text, secret) {
var iv = crypto.randomBytes(16);
console.log(iv);
var cipher = crypto.createCipheriv(algorithm, new Buffer(secret),
iv);
var encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
var encrypted = encrypt("8123497494", secret);
console.log(encrypted);
And the output is:
<Buffer 94 fa a4 f4 a1 3c bf f6 d7 90 18 3f 3b db 3f b9>
94faa4f4a13cbff6d790183f3bdb3fb9:fae8b07a135e084eb91e
Code Snippet for decryption in JAVA:
public class Test {
public static void main(String[] args) throws Exception {
String s =
"94faa4f4a13cbff6d790183f3bdb3fb9:fae8b07a135e084eb91e";
String seed = "d6F3231q7d1942874322a#123nab#392";
decrypt(s, seed);
}
private static void decrypt(String s, String seed)
throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String parts[] = s.split(":");
String ivString = parts[0];
String encodedString = parts[1];
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] secretBytes = seed.getBytes("UTF-8");
IvParameterSpec ivSpec = new IvParameterSpec(hexStringToByteArray(ivString));
/*Removed after the accepted answer
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(secretBytes);*/
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
cipher.init(Cipher.DECRYPT_MODE, skey, ivSpec);
byte[] output = cipher.doFinal(hexStringToByteArray(encodedString));
System.out.println(new String(output));
}
}
Output: �s˸8ƍ�
I am getting some junk value in the response. Tried a lot of options, but none of them seem to be working. Any lead/help is appreciated.
In your JS code, you're using the 32-character string d6F3231q7d19428743234#123nab#234 directly as the AES key, with each ASCII character directly mapped to a single key byte.
In the Java code, you're instead first hashing the same string with MD5, and then using the MD5 output as the AES key. It's no wonder that they won't match.
What you probably should be doing, in both cases, is either:
randomly generating a string of 32 bytes (most of which won't be printable ASCII characters) and using it as the key; or
using a key derivation function (KDF) to take an arbitrary input string and turn it into a pseudorandom AES key.
In the latter case, if the input string is likely to have less than 256 bits of entropy (e.g. if it's a user-chosen password, most of which only have a few dozen bits of entropy at best), then you should make sure to use a KDF that implements key stretching to slow down brute force guessing attacks.
Ps. To address the comments below, MD5 outputs a 16-byte digest, which will yield an AES-128 key when used as an AES SecretKeySpec. To use AES-256 in Java, you will need to provide a 32-byte key. If trying to use a 32-byte AES key in Java throws an InvalidKeyException, you are probably using an old version of Java with a limited crypto policy that does not allow encryption keys longer than 128 bits. As described this answer to the linked question, you will either need to upgrade to Java 8 update 161 or later, or obtain and install an unlimited crypto policy file for your Java version.
In the Java code you are taking the MD5 hash of secret before using it as a key:
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(secretBytes);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
Whereas, in your NodeJS code, you don't do this anywhere. So you're using two different keys when encrypting and decrypting.
Don't copy and paste code without understanding it. Especially crypto code.
Faced with the same task (but with 128, it easy to adapt for 256), here is working Java/NodeJs code with comments.
It's additionally wrapped to Base64 to readability, but it's easy to remove if you would like.
Java side (encrypt/decrypt) :
import java.lang.Math; // headers MUST be above the first class
import java.util.Base64;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
// one class needs to have a main() method
public class MyClass
{
private static void log(String s)
{
System.out.print("\r\n"+s);
}
public static SecureRandom IVGenerator() {
return new SecureRandom();
}
// arguments are passed using the text field below this editor
public static void main(String[] args)
{
String valueToEncrypt = "hello, stackoverflow!";
String key = "3e$C!F)H#McQfTjK";
String encrypted = "";
String decrypted = "";
//ENCODE part
SecureRandom IVGenerator = IVGenerator();
byte[] encryptionKeyRaw = key.getBytes();
//aes-128=16bit IV block size
int ivLength=16;
byte[] iv = new byte[ivLength];
//generate random vector
IVGenerator.nextBytes(iv);
try {
Cipher encryptionCipher = Cipher.getInstance("AES/CTR/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptionKeyRaw, "AES"), new IvParameterSpec(iv));
//encrypt
byte[] cipherText = encryptionCipher.doFinal(valueToEncrypt.getBytes());
ByteBuffer byteBuffer = ByteBuffer.allocate(ivLength + cipherText.length);
//storing IV in first part of whole message
byteBuffer.put(iv);
//store encrypted bytes
byteBuffer.put(cipherText);
//concat it to result message
byte[] cipherMessage = byteBuffer.array();
//and encrypt to base64 to get readable value
encrypted = new String(Base64.getEncoder().encode(cipherMessage));
} catch (Exception e) {
throw new IllegalStateException(e);
}
//END OF ENCODE CODE
log("encrypted and saved as Base64 : "+encrypted);
///DECRYPT CODE :
try {
//decoding from base64
byte[] cipherMessageArr = Base64.getDecoder().decode(encrypted);
//retrieving IV from message
iv = Arrays.copyOfRange(cipherMessageArr, 0, ivLength);
//retrieving encrypted value from end of message
byte[] cipherText = Arrays.copyOfRange(cipherMessageArr, ivLength, cipherMessageArr.length);
Cipher decryptionCipher = Cipher.getInstance("AES/CTR/NoPadding");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(encryptionKeyRaw, "AES");
decryptionCipher.init(Cipher.DECRYPT_MODE,secretKeySpec , ivSpec);
//decrypt
byte[] finalCipherText = decryptionCipher.doFinal(cipherText);
//converting to string
String finalDecryptedValue = new String(finalCipherText);
decrypted = finalDecryptedValue;
} catch (Exception e) {
throw new IllegalStateException(e);
}
log("decrypted from Base64->aes128 : "+decrypted);
//END OF DECRYPT CODE
}
}
It could be easy be tested by online java compilers (this example prepared on https://www.jdoodle.com/online-java-compiler).
NodeJs decrypt side :
const crypto = require('crypto');
const ivLength = 16;
const algorithm = 'aes-128-ctr';
const encrypt = (value, key) => {
//not implemented, but it could be done easy if you will see to decrypt
return value;
};
function decrypt(value, key) {
//from base64 to byteArray
let decodedAsBase64Value = Buffer.from(value, 'base64');
let decodedAsBase64Key = Buffer.from(key);
//get IV from message
let ivArr = decodedAsBase64Value.slice(0, ivLength);
//get crypted message from second part of message
let cipherTextArr = decodedAsBase64Value.slice(ivLength, decodedAsBase64Value.length);
let cipher = crypto.createDecipheriv(algorithm, decodedAsBase64Key, ivArr);
//decrypted value
let decrypted = cipher.update(cipherTextArr, 'binary', 'utf8');
decrypted += cipher.final('utf8');
return decrypted;
}
I have done Encryption with ,
public static String encrypt(String plainText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedString = Base64.encodeToString(cipherText, Base64.NO_WRAP);
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it's working well.
but part of Decryption gives Error like,
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
W/System.err: at com.android.org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:701)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1111)
decrypt Code like,
public static String decrypt(String encryptedText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherText = Base64.decode(encryptedText,Base64.NO_WRAP);
String decryptedString = new String(cipher.doFinal(cipherText),"UTF-8");
return decryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
here what is the problem? How can i solve this Issue?
It is likely that your secret_key value contains bytes which are not well represented in the ambiguous encoding you're using. When you call String#getBytes() without specifying an encoding, you get the system default, which can vary.
You should use hexadecimal encoding whenever you represent your key as a String. This will be consistent across serialization/deserialization on every platform. There are many standard implementations of this encoding/decoding process available (i.e. org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210"); or org.apache.commons.codec.binary.Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210".toCharArray()); which both return the raw byte[]).
Some side notes:
You are using ECB mode of operation, which is extremely susceptible to frequency analysis for cryptanalysis and is effectively deprecated aside from toy crypto demonstrations. I suggest you use CBC, CTR, or GCM.
You do not provide an initialization vector (IV), so the same message encrypted with the same key will always yield identical cipher text. Use a unique and non-predictable IV for every encryption operation by generating 16 bytes from SecureRandom and populating it into an IvParameterSpec. You can prepend the IV bytes to the cipher text and transport/store it in the clear.
Your cipher text is not authenticated, allowing for malicious users to both manipulate encrypted data and to attempt decryption via padding oracle/CCA attacks. Use an authenticated encryption with associated data (AEAD) mode like GCM, or use an HMAC/SHA-256 message authentication code (MAC) over the cipher text, and verify it using a constant-time equals method before attempting any decryption.
You do not need to provide the mode of operation or padding scheme when instantiating a key. SecretKey key = new SecretKeySpec(keyData, "AES"); is sufficient.
I'm implementing 3DES EDE3 with 3 different keys each of 56 bits, EK3 DK2 Ek1 plain text, 168 bits. But i don't know how to do it. i'm bigger to java.
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
SecretKey k3 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
String decryption = desDecryption(firstEncryption, k2);
String secondEncryption = desEncryption(decryption, k3);
System.out.println("secondEncryption: "+secondEncryption);
System.out.println("firstEncryption: "+firstEncryption);
System.out.println("decryption: "+decryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
ex.printStackTrace();
}
keyGen.init(168); // key length 112 for two keys, 168 for three keys
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
String encryptedString = new BASE64Encoder().encode(cipher.doFinal(strToEncrypt.getBytes()));
return encryptedString;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
String decryptedString = new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(strToDecrypt)));
return decryptedString;
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
I'm getting following error. tell me what wrong in this. plz help us.
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java
:294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:55)
at DESedeEncryption.main(DESedeEncryption.java:17)
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:41)
at DESedeEncryption.main(DESedeEncryption.java:18)
secondEncryption: null
firstEncryption: sP0ySxvxaUP+0a9l5qWSLQ==
decryption: null
Thanks in advance
Your code of your main() method doesn't make any sense. You're trying to decrypt with k2 a message which has been encrypted with k1. That can't work.
You don't seem to understand what DESede does. You don't need to encrypt then decrypt then encrypt with 3 different keys. You simply provide a key that has 3 times the length of a simple DES key, and the DESede cipher splits the key in 3 parts and does the three steps for you. So, as with any other Cipher, to check that it works fine, you should simply do:
SecretKey key = generateKey();
byte[] clearText = ...;
byte[] encrypted = encrypt(clearText, key);
byte[] decrypted = decrypt(encrypted, key);
// decrypted should contain the same bytes as clearText
You seem to misunderstand 3DES. The internal algorithm performs the encrypt/decrypt/encrypt operations already, so you don't need to perform that yourself. Just pass a key that's 168 bits long (3 × 56-bit) into your cipher and it'll do the job.
Additionally, the conversion to Base64 is failing. When you're running this:
String encryptedString = new BASE64Encoder().encode(cipher.doFinal(strToEncrypt.getBytes()));
You're actually calling toString() on the result of doFinal() before it's passed to encode(), causing it to return the Java reference rather than the actual contents of the array. Someone else made exactly the same mistake in an earlier question.
That said, you're using ECB for your block cipher mode. ECB (or Electronic Codebook) mode works by running each cryptographic transform on every block independently, resulting in the same ciphertext block being produced for identical ciphertext blocks when the same key is used. Wikipedia's entry on this has a great visual representation of how this kind of cryptographic process is broken.
I suggest using Cipher Block Chaining (CBC) mode instead, which combines each plaintext block with the previous ciphertext block. This ensures that equal blocks of plaintext don't produce the same blocks of ciphertext. The additional requirement here is an Initialisation Vector (IV), which is a unique random value used as the "previous block" for the first plaintext block (which obviously can't have a previous ciphertext block - it's the first one). The IV doesn't have to be secret, so you just generate it, use it, and prepend it to the encrypted message. On decryption you strip that value off, then use it to decrypt along with the key.
First of all I wanna say thank you...
I wrote a program which one is doing encryption and decryption with Enum.
Enum has AES,BlowFish,DESede. My program will support these 3 encryption algorithm.
Then I wanted to Generate a SecretKey with SecretKeyFactory.But I think,I made a mistake to generate a key. (Obviously I loose myself in code.I have no idea about what can I do...)
My Code is below. This program's purpose is;
Users will write encryption and decryption method parameters. (Text,Encryption Algorithm)
Algorithm type will choose in Enum type. (Enum has 3 algorithm format)
According to the entered Encryption Type,program will encrypt entered text.
I know my code is really terrible. It has lots of unnecessary declaration and logical mistakes.
Code is working fine sometimes,sometimes will crash.
EDIT = Question is my code doesnt work always. Sometimes gives error. Error is = javax.crypto.BadPaddingException: Given final block not properly padded
Thank you for answering.
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class SymetricAlgorithms {
private static enum algorithms { //Enum declaration 3 encryption types here
AES, BlowFish, DESede;
}
private static String data = "HOWCANISOLVETHIS"; //this will be encrypt and decrypt
public static void main(String[] args) throws Throwable {
SecretKey kgen = GenerateKey(); // Create a key.
String encrypText = encrypt(kgen, data, algorithms.AES); //encrypt method calling here.
String decrypText = dencypt(kgen, encrypText, algorithms.AES);//decrypt method calling here.
System.out.println("plaintext = " + data + " key = " + kgen
+ "\nEncryptedText = " + encrypText
+ "\nDecryptedText = " + decrypText);
}
public static String dencypt(SecretKey inKey, String text, algorithms eValue)throws Throwable {//decryption
try {
byte[] text2 = text.getBytes(); //convert from parameters TEXT to Bytes
Cipher cipher = Cipher.getInstance("AES"); //Cipher initialize and choose encryption method (AES)
cipher.init(Cipher.DECRYPT_MODE, inKey); //cipher process
byte plainTextByte[] = new byte[20]; //Creating byte array
plainTextByte =cipher.doFinal(text2);//using byte array to assign ciphers result
System.out.println(plainTextByte);
return new String(plainTextByte);
} catch (Exception e) {
System.err.println("Data Cant Decrypted !");
e.printStackTrace();
}
return null;
}
public static String encrypt(SecretKey inKey, String text, algorithms eValue)
throws Throwable {
try {
Cipher cipher = null; //cipher declaration
switch (eValue) {//Enum. 3 types here and control structure for Users choosing encryption type is acceptable
case AES:cipher = Cipher.getInstance("AES");
break;
case BlowFish:Cipher cipher2 = Cipher.getInstance("BlowFish");
cipher = cipher2;
break;
case DESede:Cipher cipher3 = Cipher.getInstance("DESede");
cipher=cipher3;
break;
default:
System.out.println("Unexpectable value input.");
break;
}
System.out.println(inKey);
//Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, inKey);
byte[] ciphertext = cipher.doFinal(text.getBytes("UTF-8"));//cipher result is assign to byte array
System.out.println(ciphertext);
return new String(ciphertext);
} catch (Exception e) {
System.err.println("Unexpectable algorithm type !");
e.printStackTrace();
}
return null;
}
public static SecretKey GenerateKey() throws Throwable {//Generate a key for using crypt
//could sb explain these? =D I loose myself. I combined codes from finding internet...Failed...
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
byte bytes[] = new byte[20];
prng.nextBytes(bytes);
String passwordTemp = prng.toString();
String saltTemp = passwordTemp;
char[] password = passwordTemp.toCharArray();
byte[] salt = saltTemp.getBytes();
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
return secret;
} catch (Exception e) {
System.err.println("Key cant be generated !");
e.printStackTrace();
}
return null;
}
}
The theme of the problem is misunderstanding of the relationship between Strings and bytes. At the end of the encrypt method, what do you think these two lines do:
byte[] ciphertext = cipher.doFinal(...
return new String(ciphertext);
The last line takes the encrypted bytes, which could be almost anything, and attempts to interpret those bytes as encoding some characters of a string. Using what encoding? String constructor with no character encoding argument uses system default encoding, which depends on JVM/OS/Locale. Lets say it is UTF-8. Are you guaranteed that there will actually be some character for the encrypted bytes? Answer: NO. Will you get the same bytes back, when you take the resulting string and call .getBytes("UTF-8"). Answer: No, there are mutliple byte sequences encoding the same characters, thus new String(bytes, "UTF-8").getBytes("UTF-8") is not guaranteed to return the bytes you started with.
In summary, don't attempt to interpret arbitrary bytes as a string. Make your encrypt method return byte[], and your decryp method take an array of bytes to decode-- then it will work.
It is not necessary to make your program work, but if you must represent the encrypted bytes as a string, consider base64 encoding, or hexadecimal encoding -- these encodings uniquely map every possible byte (or sequence of bytes) to a string.
UPDATE: here is a more concise generateKey() method. It allows you to pass the password in as an argument.
public static SecretKey generateKey(String password) {
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
byte saltBytes[] = new byte[20];
secureRandom.nextBytes(saltBytes);
KeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = factory.generateSecret(spec);
return new SecretKeySpec(secretKey.getEncoded(), "AES");
} catch (Exception e) {
throw new IllegalStateException("Key cant be generated !");
}
}
I am new in cipher technology. I found this code to do Symmetric Encryption.
byte[] key = //... secret sequence of bytes
byte[] dataToSend = ...
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(dataToSend);
Its working. Here I can use my own password. And thats what exactly I needed. But I dont know how to do 128 or 256 Symmetric Enctryption.
How can I use 128 and 256 key into my code ?
Whether AES uses 128 or 256 bit mode depends on size of your key, which must be 128 or 256 bits long. Typically you don't use your password as a key, because passwords rarely have exact length as you need. Instead, you derive encryption key from your password by using some key derivation function.
Very simple example: take MD5 of your password to get 128-bit key. If you want 256-bit key, you can use SHA-256 to get 256-bit hash of your password. Key-derivation functions usually run this hashing several hundreds time and use extra salt as well. Check out http://en.wikipedia.org/wiki/Key_derivation_function for details.
Also note: to run encryption stronger than 128-bit you will need to download and install 'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6' from http://www.oracle.com/technetwork/java/javase/downloads/index.html.
The Answer for 128 bit
The following method is to encrypt a string (valueEnc) with AES encryption:
private static final String ALGORITHM = "AES";
public String encrypt(final String valueEnc, final String secKey) {
String encryptedVal = null;
try {
final Key key = generateKeyFromString(secKey);
final Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
final byte[] encValue = c.doFinal(valueEnc.getBytes());
encryptedVal = new BASE64Encoder().encode(encValue);
} catch(Exception ex) {
System.out.println("The Exception is=" + ex);
}
return encryptedVal;
}
The next method will decrypt the AES encrypted string (encryptedVal):
public String decrypt(final String encryptedValue, final String secretKey) {
String decryptedValue = null;
try {
final Key key = generateKeyFromString(secretKey);
final Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
final byte[] decorVal = new BASE64Decoder().decodeBuffer(encryptedValue);
final byte[] decValue = c.doFinal(decorVal);
decryptedValue = new String(decValue);
} catch(Exception ex) {
System.out.println("The Exception is=" + ex);
}
return decryptedValue;
}
The secKey is a 128-bit key, which is encoded in the BASE64Encoder. The BASE64Decoder in the following method generates an appropriate 128-bit key
private Key generateKeyFromString(final String secKey) throws Exception {
final byte[] keyVal = new BASE64Decoder().decodeBuffer(secKey);
final Key key = new SecretKeySpec(keyVal, ALGORITHM);
return key;
}
You can use a simple KeyGenerator object like this:
KeyGenerator generator = KeyGenerator.getInstance("AES/CTR/PKCS5PADDING");
generator.init(128);
SecretKey key = generator.generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
...
From Java's docs for Cipher.init(...):
public final void init(int opmode,
Key key)
Throws:
InvalidKeyException - if the given key is inappropriate for initializing
this cipher, or if this cipher is
being initialized for decryption and
requires algorithm parameters that
cannot be determined from the given
key, or if the given key has a keysize
that exceeds the maximum allowable
keysize (as determined from the
configured jurisdiction policy files).
To me, this means that, as Martijn Courteaux said in his comment, you should use a key of 256 bits (i.e. initialize the SecretKeySpec with a byte array containing 32 bytes), and the cipher will accept it and use it, or reject it and throw an exception if its size is not acceptable.
If you get an exception, it's probably because you have not installed the unlimited strength crypto files, (the default JDK install allows 128 bit keys as documented in this crypto spec document). Download unlimited strength crypto package here.
public class CipherUtils
{
private static byte[] key = {
0x74, 0x68, 0x69, 0x73, 0x49, 0x73, 0x41, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79
};//"thisIsASecretKey";
public static String encrypt(String strToEncrypt)
{
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final String encryptedString = Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes()));
return encryptedString;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
}