I am trying to encrypt a Client' name (string format) storing it in a database and then retrieving it and decrypting it. As i need to avoid any third part libraries, i have used classes which are readily available with Java distribution.
The process was working fine, until I encountered a name with a special character (Ascii : 48910). This was geting displayed as a question mark(?). The encryption and descryption went fine, but after the decryption the special character was replaced with the question mark.
So i changed the Encoding format from 'UTF-8' to 'ISO-8859-1'. This solved the display problem, but still the special character gets replaced after decryption.
The code being used and the output is given below (i have removed the unnecessary code):
package crypt;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.xml.bind.DatatypeConverter;
public class SecretKeyEncryptionExample {
private static final String FORMAT = "ISO-8859-1";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
SecretKey key;
public SecretKeyEncryptionExample() throws Exception {
String myEncryptionKey = "4A144BEBF7E5E7B7DCF26491AE79C54C768C514CF1547D23";
ks = new DESedeKeySpec(myEncryptionKey.getBytes(FORMAT));
skf = SecretKeyFactory.getInstance(DESEDE_ENCRYPTION_SCHEME);
cipher = Cipher.getInstance(DESEDE_ENCRYPTION_SCHEME);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) throws Exception {
String encryptedString = null;
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = DatatypeConverter.printBase64Binary(encryptedText);
return encryptedString;
}
public String decrypt(String encryptedString) throws Exception {
String decryptedText = null;
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = DatatypeConverter.parseBase64Binary(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText = new String(plainText);
return decryptedText;
}
public static void main(String args[]) throws Exception {
SecretKeyEncryptionExample td = new SecretKeyEncryptionExample();
String target = "Expendable" + getSpecialCharacter(49810) + "s Pte Ltd";
String encrypted = td.encrypt(target);
String decrypted = td.decrypt(encrypted);
PrintStream out = new PrintStream(System.out, true, FORMAT);
out.println("String To Encrypt: " + target);
out.println("Encrypted String: " + encrypted);
out.println("Decrypted String: " + decrypted);
}
public static String getSpecialCharacter(int code) {
Charset charSet = Charset.forName(FORMAT);
String specialCharacter = new String(new byte[] { (byte) code }, charSet);
specialCharacter = String.format("%s", specialCharacter);
return specialCharacter;
}
}
OUTPUT:
String To Encrypt: Expendable’s Pte Ltd
Encrypted String: TAAJuF7KOmBZHBXFHsW0FB9YBwH7Tcif
Decrypted String: Expendable?s Pte Ltd
Please let know how the decryption can be attained, without getting the special character replaced.
I think you should specify your encoding every time you go from a string to a byte array and back. In particular, this line:
decryptedText = new String(plainText);
should read:
decryptedText = new String(plainText, FORMAT);
Otherwise you rely on your environment's encoding, which in all likelihood differs from FORMAT and result in the special character being printed as "?".
Some things which may be useful to know.
System.out.println((int) getSpecialCharacter(49810).charAt(0));
prints
146
This is the character you are actually creating here.
System.out.println("The Falcon" + (char) 146 + "s Hangar Pte Ltd");
prints
The Falcon’s Hangar Pte Ltd
I think the problem is that you get the bytes using the ISO-8859-1 character set with
byte[] plainText = unencryptedString.getBytes(FORMAT);
but when you turn it back into a String you use the system default.
decryptedText = new String(plainText);
I suspect this should be
decryptedText = new String(plainText, FORMAT); // use the same Charset
Related
PHP Encrypt Function
$privateKey = "1234567812345678";
$iv = "1234567812345678";
$data = "Test string";
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv);
echo(base64_encode($encrypted));
Result: iz1qFlQJfs6Ycp+gcc2z4w==
When I try to decrypt this result in Java using the function below, all I get back is ì�š#ÔBKxnfÈ~¯Ô'M while I am expecting "Test string". Any ideas where I am wrong? Thanks
public static String decrypt() throws Exception{
try{
String Base64EncodedText = "iz1qFlQJfs6Ycp+gcc2z4w==";
String decodedText = com.sun.xml.internal.messaging.saaj.util.Base64.base64Decode(Base64EncodedText);
String key = "1234567812345678";
String iv = "1234567812345678";
javax.crypto.spec.SecretKeySpec keyspec = new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES");
javax.crypto.spec.IvParameterSpec ivspec = new javax.crypto.spec.IvParameterSpec(iv.getBytes());
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decrypted = cipher.doFinal(decodedText.getBytes());
String str = new String(decrypted);
return str;
}catch(Exception e){
return null;
}
}
EDIT: As of Java 8 Java now includes an acceptable Base64 class, java.util.Base64.
This line
String decodedText = com.sun.xml.internal.messaging.saaj.util.Base64.base64Decode(Base64EncodedText);
looks wrong. Instead, use the apache commons codec classes or the Harder base64 class. Also the default padding used by mcrypt, zero padding, is arguably wrong and makes it difficult to use the results in other languages. The users comments section for the mcrypt_encrypt web pages has examples of how to do this.
Here is small example that uses the apache commons classes to decrypt your string.
import java.nio.charset.Charset;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
public class AESToy3 {
private static final Charset ASCII = Charset.forName("US-ASCII");
public static void main(String[] args) throws Exception {
String base64Cipher = "iz1qFlQJfs6Ycp+gcc2z4w==";
byte [] cipherBytes = Base64.decodeBase64(base64Cipher);
byte [] iv = "1234567812345678".getBytes(ASCII);
byte [] keyBytes = "1234567812345678".getBytes(ASCII);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv));
byte[] result = cipher.doFinal(cipherBytes);
System.out.println(Hex.encodeHexString(result));
}
}
this produces the following output:
5465737420737472696e670000000000
which when decoded as ASCII and removing the trailing zeros gives you Test string
I would like to store an encrypted password in a Java file.
I saw at a solution using javax.crypto, but the problem with that was that the key
was being generated on the fly and it was random.
This password will be then taken and decrypted in the Java program in runtime.
Given that I am going to store an already encrypted password in a file - I want to get the
right text when decrypting it.
Is there a way to tell the javax.crypto method:
key = KeyGenerator.getInstance(algorithm).generateKey()
Can this be replaced with my own key generated once based on some private key?
Can anyone point me to some resources on how to do this?
Symmetric Key Cryptography : Symmetric key uses the same key for encryption and decryption. The main challenge with this type of cryptography is the exchange of the secret key between the two parties sender and receiver.
Example : The following example uses symmetric key for encryption and decryption algorithm available as part of the Sun's JCE(Java Cryptography Extension). Sun JCE is has two layers, the crypto API layer and the provider layer.
DES (Data Encryption Standard) was a popular symmetric key algorithm. Presently DES is outdated and considered insecure. Triple DES and a stronger variant of DES. It is a symmetric-key block cipher. There are other algorithms like Blowfish, Twofish and AES(Advanced Encryption Standard). AES is the latest encryption standard over the DES.
Steps :
Add the Security Provider : We are using the SunJCE Provider that is available with the JDK.
Generate Secret Key : Use KeyGenerator and an algorithm to generate a secret key. We are using DESede (DESede is descriptive name for 3DES implementation: DESede = DES-Encrypt-Decrypt-Encrypt = Triple DES).
Encode Text : For consistency across platform encode the plain text as byte using UTF-8 encoding.
Encrypt Text : Instantiate Cipher with ENCRYPT_MODE, use the secret key and encrypt the bytes.
Decrypt Text : Instantiate Cipher with DECRYPT_MODE, use the same secret key and decrypt the bytes.
All the above given steps and concept are same, we just replace algorithms.
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class EncryptionDecryptionAES {
static Cipher cipher;
public static void main(String[] args) throws Exception {
/*
create key
If we need to generate a new key use a KeyGenerator
If we have existing plaintext key use a SecretKeyFactory
*/
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // block size is 128bits
SecretKey secretKey = keyGenerator.generateKey();
/*
Cipher Info
Algorithm : for the encryption of electronic data
mode of operation : to avoid repeated blocks encrypt to the same values.
padding: ensuring messages are the proper length necessary for certain ciphers
mode/padding are not used with stream cyphers.
*/
cipher = Cipher.getInstance("AES"); //SunJCE provider AES algorithm, mode(optional) and padding schema(optional)
String plainText = "AES Symmetric Encryption Decryption";
System.out.println("Plain Text Before Encryption: " + plainText);
String encryptedText = encrypt(plainText, secretKey);
System.out.println("Encrypted Text After Encryption: " + encryptedText);
String decryptedText = decrypt(encryptedText, secretKey);
System.out.println("Decrypted Text After Decryption: " + decryptedText);
}
public static String encrypt(String plainText, SecretKey secretKey)
throws Exception {
byte[] plainTextByte = plainText.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedByte = cipher.doFinal(plainTextByte);
Base64.Encoder encoder = Base64.getEncoder();
String encryptedText = encoder.encodeToString(encryptedByte);
return encryptedText;
}
public static String decrypt(String encryptedText, SecretKey secretKey)
throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
byte[] encryptedTextByte = decoder.decode(encryptedText);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedByte = cipher.doFinal(encryptedTextByte);
String decryptedText = new String(decryptedByte);
return decryptedText;
}
}
Output:
Plain Text Before Encryption: AES Symmetric Encryption Decryption
Encrypted Text After Encryption: sY6vkQrWRg0fvRzbqSAYxepeBIXg4AySj7Xh3x4vDv8TBTkNiTfca7wW/dxiMMJl
Decrypted Text After Decryption: AES Symmetric Encryption Decryption
Source
Example: Cipher with two modes, they are encrypt and decrypt. we have to start every time after setting mode to encrypt or decrypt a text.
Here is a solution using the javax.crypto library and the apache commons codec library for encoding and decoding in Base64 that I was looking for:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Base64;
public class TrippleDes {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TrippleDes() throws Exception {
myEncryptionKey = "ThisIsSpartaThisIsSparta";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
public static void main(String args []) throws Exception
{
TrippleDes td= new TrippleDes();
String target="imparator";
String encrypted=td.encrypt(target);
String decrypted=td.decrypt(encrypted);
System.out.println("String To Encrypt: "+ target);
System.out.println("Encrypted String:" + encrypted);
System.out.println("Decrypted String:" + decrypted);
}
}
Running the above program results with the following output:
String To Encrypt: imparator
Encrypted String:FdBNaYWfjpWN9eYghMpbRA==
Decrypted String:imparator
KeyGenerator is used to generate keys
You may want to check KeySpec, SecretKey and SecretKeyFactory classes
http://docs.oracle.com/javase/1.5.0/docs/api/javax/crypto/spec/package-summary.html
You may want to use the jasypt library (Java Simplified Encryption), which is quite easy to use. ( Also, it's recommended to check against the encrypted password rather than decrypting the encrypted password )
To use jasypt, if you're using maven, you can include jasypt into your pom.xml file as follows:
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.3</version>
<scope>compile</scope>
</dependency>
And then to encrypt the password, you can use StrongPasswordEncryptor
public static String encryptPassword(String inputPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.encryptPassword(inputPassword);
}
Note: the encrypted password is different every time you call encryptPassword but the checkPassword method can still check that the unencrypted password still matches each of the encrypted passwords.
And to check the unencrypted password against the encrypted password, you can use the checkPassword method:
public static boolean checkPassword(String inputPassword, String encryptedStoredPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.checkPassword(inputPassword, encryptedStoredPassword);
}
The page below provides detailed information on the complexities involved in creating safe encrypted passwords.
http://www.jasypt.org/howtoencryptuserpasswords.html
Simple and easy solution :- jasypt library.
To use this library in your maven project include below dependency
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.3</version>
<scope>compile</scope>
</dependency>
There is one security advantage of using this library is it does not provide any direct method to decrypt the password.
User can not pick any ecrypted password and pass it to library to get original password. To validate any user jasypt library provide a method which accept 2 parameters i.e. input password and encrypted password, the method will return true if the password is correct and false if password is wrong.
Just create a Utility Class like this.
public class EncryptDecryptPassword {
public static String encryptPassword(String inputPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.encryptPassword(inputPassword);
}
public static boolean checkPassword(String inputPassword, String encryptedStoredPassword) {
StrongPasswordEncryptor encryptor = new StrongPasswordEncryptor();
return encryptor.checkPassword(inputPassword, encryptedStoredPassword);
}
}
Use these static methods from anywhere from your project to perform encryption.
Here is a sample I made a couple of months ago
The class encrypt and decrypt data
import java.security.*;
import java.security.spec.*;
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class TestEncryptDecrypt {
private final String ALGO = "DES";
private final String MODE = "ECB";
private final String PADDING = "PKCS5Padding";
private static int mode = 0;
public static void main(String args[]) {
TestEncryptDecrypt me = new TestEncryptDecrypt();
if(args.length == 0) mode = 2;
else mode = Integer.parseInt(args[0]);
switch (mode) {
case 0:
me.encrypt();
break;
case 1:
me.decrypt();
break;
default:
me.encrypt();
me.decrypt();
}
}
public void encrypt() {
try {
System.out.println("Start encryption ...");
/* Get Input Data */
String input = getInputData();
System.out.println("Input data : "+input);
/* Create Secret Key */
KeyGenerator keyGen = KeyGenerator.getInstance(ALGO);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.init(56,random);
Key sharedKey = keyGen.generateKey();
/* Create the Cipher and init it with the secret key */
Cipher c = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING);
//System.out.println("\n" + c.getProvider().getInfo());
c.init(Cipher.ENCRYPT_MODE,sharedKey);
byte[] ciphertext = c.doFinal(input.getBytes());
System.out.println("Input Encrypted : "+new String(ciphertext,"UTF8"));
/* Save key to a file */
save(sharedKey.getEncoded(),"shared.key");
/* Save encrypted data to a file */
save(ciphertext,"encrypted.txt");
} catch (Exception e) {
e.printStackTrace();
}
}
public void decrypt() {
try {
System.out.println("Start decryption ...");
/* Get encoded shared key from file*/
byte[] encoded = load("shared.key");
SecretKeyFactory kf = SecretKeyFactory.getInstance(ALGO);
KeySpec ks = new DESKeySpec(encoded);
SecretKey ky = kf.generateSecret(ks);
/* Get encoded data */
byte[] ciphertext = load("encrypted.txt");
System.out.println("Encoded data = " + new String(ciphertext,"UTF8"));
/* Create a Cipher object and initialize it with the secret key */
Cipher c = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING);
c.init(Cipher.DECRYPT_MODE,ky);
/* Update and decrypt */
byte[] plainText = c.doFinal(ciphertext);
System.out.println("Plain Text : "+new String(plainText,"UTF8"));
} catch (Exception e) {
e.printStackTrace();
}
}
private String getInputData() {
String id = "owner.id=...";
String name = "owner.name=...";
String contact = "owner.contact=...";
String tel = "owner.tel=...";
final String rc = System.getProperty("line.separator");
StringBuffer buf = new StringBuffer();
buf.append(id);
buf.append(rc);
buf.append(name);
buf.append(rc);
buf.append(contact);
buf.append(rc);
buf.append(tel);
return buf.toString();
}
private void save(byte[] buf, String file) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
fos.write(buf);
fos.close();
}
private byte[] load(String file) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[fis.available()];
fis.read(buf);
fis.close();
return buf;
}
}
public class GenerateEncryptedPassword {
public static void main(String[] args){
Scanner sc= new Scanner(System.in);
System.out.println("Please enter the password that needs to be encrypted :");
String input = sc.next();
try {
String encryptedPassword= AESencrp.encrypt(input);
System.out.println("Encrypted password generated is :"+encryptedPassword);
} catch (Exception ex) {
Logger.getLogger(GenerateEncryptedPassword.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
If you use a static key, encrypt and decrypt always give the same result;
public static final String CRYPTOR_KEY = "your static key here";
byte[] keyByte = Base64.getDecoder().decode(CRYPTOR_KEY);
key = new SecretKeySpec(keyByte, "AES");
Why does not this AES encryption work? I've written it in Java to test, but I am not able to decrypt. I get garbage upon decryption. Why? Its so simple - In the main method, print plain text, encrypt, print cipher text, decrypt, print plain text again. Am I doing something wrong? Please help me figure out the problem.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
String cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String cipherText = new String(cipher.doFinal(plainText.getBytes()));
return cipherText;
}
public static String decrypt(String cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String plainText = new String(cipher.doFinal(cipherText.getBytes()));
return plainText;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
The output of the cipher is a sequence of random-looking bytes. You have no guarantee that these bytes will be a valid encoding for a character string in whatever is your system's default encoding. So this line:
String cipherText = new String(cipher.doFinal(.....));
is likely to lose information that you'll need for decryption.
Therefore you will not get the right bytes reconstructed in your decrypt operation. For example, if your default encoding is UTF-8, it is overwhelmingly unlikely that the correct ciphertext is something that String.getBytes() is even able to produce.
Two things:
No padding can only work if you use input that is an exact mulitple of your key size, which is 128 bit or 16 bytes. So in your particular case "Hello World!!!!!".getBytes() is actually a multiple of 16, but this is of course not true for arbitrary Strings.
Use "AES/CBC/PKCS5Padding" instead to solve this issue.
Do not turn your encrypted data into a String - this will and change the encrypted output. There's no guarantee that new String(byte[]).getBytes() returns the exact same byte array!
So you should leave the encrypted data as what it is - a stream of bytes. Thus encrypt should return byte[] instead and decrypt should take byte[] as input - this is a working example:
public class NewClass {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
byte[] cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
// -> Hello World!!!!
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(plainText.getBytes());
}
public static String decrypt(byte[] cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return new String(cipher.doFinal(cipherText));
}
You need to create the SecretKeySpec object once and use it for both encrypt and decrypt. Currently the code is creating two different keys for each operation and this will definitely lead to incorrect results.
I am trying to translate the following (working) Java code to Ruby.
public static final String PROVIDER = "BC";
public static final int IV_LENGTH = 16;
private static final String HASH_ALGORITHM = "SHA-512";
private static final String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String SECRET_KEY_ALGORITHM = "AES";
public String decrypt(SecretKey secret, String encrypted) {
Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
String ivHex = encrypted.substring(0, IV_LENGTH * 2);
String encryptedHex = encrypted.substring(IV_LENGTH * 2);
IvParameterSpec ivspec = new IvParameterSpec(HexEncoder.toByte(ivHex));
decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
byte[] decryptedText = decryptionCipher.doFinal(HexEncoder.toByte(encryptedHex));
String decrypted = new String(decryptedText, "UTF-8");
return decrypted;
}
My (not working) Ruby code is this:
require 'openssl'
require 'digest/sha2'
SECRET = "MY PASSWORD AS RAW TEXT"
IV_LENGHT = 16
encoded = "45D0EC4D910C0A6FF67325FF7362DCBC4613B6F3BFDFE35930D764FB1FE62251"
iv = encoded.slice(0, IV_LENGHT * 2)
e = encoded.slice(IV_LENGHT*2..-1)
binary_iv = iv.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT)
binary_e = e.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT)
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
c.decrypt
c.key = Digest::SHA256.digest(SECRET).slice(0, IV_LENGHT* 2 )
c.iv = binary_iv
d = c.update(binary_e)
d << c.final
puts "decrypted: #{d}\n"
I have tried the binary and non binary versions, with no luck.
Someone can point to the problem?
Based on the title here, I am going to assume that you want to be able to encrypt a message in Java, and then decrypt that message in Ruby, using password-based AES-CBC encryption.
Now, the openssl standard library in Ruby readily supports password-based key derivation function 2 based on PKCS5. You can greatly simplify your Ruby decryption code if you leverage this in your Java encryption.
Here is how you would encrypt using PBKDF2 in PKCS5 in Java:
// in Java-land
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
...
static String printHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", (b & 0xFF)));
}
return sb.toString();
}
public static Map<String,String> encrypt(String msg, String pwd, byte[] salt)
throws Exception {
Map<String,String> retval = new HashMap<String,String>();
// prepare to use PBKDF2/HMAC+SHA1, since ruby supports this readily
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
// our key is 256 bits, and can be generated knowing the password and salt
KeySpec spec = new PBEKeySpec(pwd.toCharArray(), salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// given key above, our cippher will be aes-256-cbc in ruby/openssl
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
// generate the intialization vector
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
retval.put("iv", printHex(iv));
byte[] ciphertext = cipher.doFinal(msg.getBytes("UTF-8"));
retval.put("encrypted", printHex(ciphertext));
return retval;
}
public static void main(String[] args) throws Exception {
String msg = "To Ruby, from Java, with love...";
String pwd = "password";
String salt = "8 bytes!"; // in reality, you would use SecureRandom!
System.out.println("password (plaintext): " + pwd);
System.out.println("salt: " + salt);
Map<String,String> m = encrypt(msg, pwd, salt.getBytes());
System.out.println("encrypted: " + m.get("encrypted"));
System.out.println("iv: " + m.get("iv"));
}
Running the above will result in something like the following output.
password (plaintext): password
salt: 8 bytes!
encrypted: 4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d
iv: ecbc985b3550edc977a17acc066f2192
Hex strings are used for the encrypted message and initialization vector since you can use OpenSSL to verify the encryption/decryption process (highly recommended).
Now from a Ruby program, you would use the AES-256-CBC cipher, and derive the secret key from the password and salt strings (not byte[] as per Java). Using the output from the above-mentioned Java program, we have:
# from Ruby-land
require 'openssl'
d = OpenSSL::Cipher.new("AES-256-CBC")
d.decrypt
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "8 bytes!", 1024, d.key_len)
d.key = key
d.iv = "ecbc985b3550edc977a17acc066f2192".scan(/../).map{|b|b.hex}.pack('c*')
data = "4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d".scan(/../).map{|b|b.hex}.pack('c*')
d.update(data) << d.final
=> "To Ruby, from Java, with love..."
NOTE: The Ruby part of this code pretty much comes verbatim from the Japanese documentation on the openssl standard library.
I once had a similar problem with CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; and decryption through the openSSL-library in C which I could't solve. I avoided the problem by using "AES/CBC/NoPadding" and by adding a particular padding to the plaintext manually.
Is there some Java equivalent of Convert.FromBase64String which
Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array.
I have tried:
com.lutris.util.Convert. but it gives me signed values
encode to string then convert to unsigned bytes, but then it gives me completely wrong output (wrong length and data).
Will be grateful for any suggestions!
In general, chances are good that if the standard Java libraries don't have something you need, the Apache Commons framework does. In this case, you want the decodeBase64 method from commons.codec.binary.Base64.
The key and the initial vector are converted using Convert.FromBase64String inAES encryption in C#.
In java, you can use DatatypeConverter.parseBase64Binary(string) method. In order to use it import import javax.xml.bind.*;
Here is the example program
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.*;
class AESEncryption {
private static final String key = "type your key here.(base64)";
private static final String initVector = "type your initial vector here.(base64)";
static byte[] encodedKey = DatatypeConverter.parseBase64Binary(key);
static byte[] encodedIV = DatatypeConverter.parseBase64Binary(initVector);
public static String encrypt(final String value) {
try {
IvParameterSpec iv = new IvParameterSpec(encodedIV);
SecretKeySpec skeySpec = new SecretKeySpec(encodedKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
} catch (final Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(final String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(encodedIV);
SecretKeySpec skeySpec = new SecretKeySpec(encodedKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));
return new String(original);
} catch (final Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void main(final String[] args) {
String originalString = "Here is your text to be encrypted.";
System.out.println("Original String to encrypt - " + originalString);
String encryptedString = encrypt(originalString);
System.out.println("Encrypted String - " + encryptedString);
String decryptedString = decrypt(encryptedString);
System.out.println("After decryption - " + decryptedString);
}
}