import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
import com.Ostermiller.util.Base64;
import com.Ostermiller.util.MD5;
import java.net.URLEncoder;
public class EnDecryptor {
public static void addProvider()
{
if (Security.getProvider("IBMJCE") == null) {
// IBMJCE is not installed, install it.
try
{
Security.addProvider
((Provider)Class.forName("com.ibm.crypto.provider.IBMJCE").newInstance());
}
catch (Exception ex) {
AuthLogger.logFatal("EnDecryptor:addProvider():Cannot install provider: " + ex.getMessage());
}
}
}
public static String encrypt(String word)
{
addProvider();
String encWord="";
byte[] encryptedWordbytes=null;
try
{
if (null == word)
{
throw new NullPointerException("EnDecryptor:encrypt(): No string to be encrypted provided!");
}
AuthLogger.logDebug("EnDecryptor:encrypt():Generating an encryption key...");
encryptedWordbytes = MD5.getHash(word);
AuthLogger.logDebug("EnDecryptor:encrypt():MD5 HASH length:"+encryptedWordbytes.length);
AuthLogger.logDebug("EnDecryptor:encrypt():MD5 HASH :"+new String(encryptedWordbytes));
// Create a Rijndael key
SecretKeySpec KeySpec = new SecretKeySpec(encryptedWordbytes, "AES");
AuthLogger.logDebug("EnDecryptor:encrypt():Done generating the key...");
Cipher cipherKey =Cipher.getInstance("AES/CBC/PKCS5Padding", "IBMJCE");
byte[] iv = new byte[16];
IvParameterSpec spec = null;
for(int i=0;i<16;i++)
{
iv[i]=(byte)i;
}
spec = new IvParameterSpec(iv);
cipherKey.init(Cipher.ENCRYPT_MODE, KeySpec, spec);
byte[] encryptedDataBytes = cipherKey.doFinal(word.getBytes());
String base64data = new String(Base64.encode(encryptedDataBytes));
AuthLogger.logDebug("EnDecryptor:encrypt():BASE64DATA="+base64data);
byte[] encryptedKeyBytes = MD5.getHash(encryptedDataBytes);
SecretKeySpec KeySpec2 = new SecretKeySpec(encryptedKeyBytes, "AES");
Cipher cipherKey2 = Cipher.getInstance("AES/CBC/PKCS5Padding", "IBMJCE");//Cipher.getInstance(DataEncryptDecrypt.AlgEnc);
cipherKey2.init(Cipher.ENCRYPT_MODE, KeySpec2, spec);
byte[] encryptedkey = cipherKey2.doFinal(encryptedWordbytes);
String base64Key = new String(Base64.encode(encryptedkey));
AuthLogger.logDebug("EnDecryptor:encrypt():BASE64Key="+base64Key);
String parm1 = "Data=" + URLEncoder.encode(base64data, "UTF-8") ;//$$$ encode(base64data);
String parm2 = "A=" + URLEncoder.encode(base64Key, "UTF-8") ;//$$$ encode(base64Key);
//encWord="Data="+parm1+"&A="+parm2;
encWord=parm1+"&"+parm2;
}catch(Exception e)
{
e.printStackTrace();
}
return encWord;
}
public static String decrypt(String encData, String encKey)
{
addProvider();
String decryptedData="";
byte[] abKeysKey=null;
try
{
byte[] abEncryptedKeys=(Base64.decode(encKey.getBytes()));
if (null == encData)
{
throw new NullPointerException("EnDecryptor:decrypt(): No data to be decryopted provided!");
}
AuthLogger.logDebug("EnDecryptor:decrypt():Generating a the HASH of the data...");
abKeysKey = MD5.getHash(Base64.decode(encData.getBytes()));
// Create a Rijndael key
SecretKeySpec KeySpec = new SecretKeySpec(abKeysKey, "AES");
Cipher cipherKey = Cipher.getInstance("AES/CBC/PKCS5Padding", "IBMJCE");//Cipher.getInstance(DataEncryptDecrypt.AlgEnc);
IvParameterSpec spec = null;
byte[] iv = new byte[16];
for(int i=0;i<16;i++)
{
iv[i]=(byte)i;
}
spec = new IvParameterSpec(iv);
cipherKey.init(Cipher.DECRYPT_MODE, KeySpec, spec);
byte[] abKeys = cipherKey.doFinal(abEncryptedKeys);
String base64key = new String(Base64.encode(abKeys));
AuthLogger.logDebug("EnDecryptor:decrypt():BASE64 DECODED KEY="+base64key);
//byte[] encryptedKeyBytes = MD5.getHash(encryptedDataBytes);
SecretKeySpec KeySpec2 = new SecretKeySpec(abKeys, "AES");
Cipher cipherKey2 = Cipher.getInstance("AES/CBC/PKCS5Padding", "IBMJCE");//Cipher.getInstance(DataEncryptDecrypt.AlgEnc);
cipherKey2.init(Cipher.DECRYPT_MODE, KeySpec2, spec);
byte[] decodedData = cipherKey2.doFinal(Base64.decode(encData.getBytes()));
decryptedData= new String(decodedData);
AuthLogger.logDebug("EnDecryptor:decrypt():decoded data="+decryptedData);
}catch(Exception e)
{
e.printStackTrace();
}
return decryptedData;
}
}
in this code i can mostly encode A-Z and a-z, 0-9 and +,/ but i cannot encode any special characters like(~,#,!, etc) or international characters like(ê,etc)
can anyone suggest anything because i need to encode and decode characters like these any help will be appreciated.
New edit
if i use import java.io.IOException; will it solve my issue?
i know it's not good to use sun packages but i have done this it encodes mostly all the spacial and international characters except one or two here is the code
import java.io.IOException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
// Java Base64 Encoder / Java Base64 Decoder
public class Base64Test {
public static void main(String[] args) {
BASE64Decoder decoder = new BASE64Decoder();
BASE64Encoder encoder = new BASE64Encoder();
try {
String encodedBytes = encoder.encodeBuffer("(####&&&&&)".getBytes());
System.out.println("encodedBytes " + encodedBytes);
byte[] decodedBytes = decoder.decodeBuffer(encodedBytes);
System.out.println("decodedBytes " + new String(decodedBytes));
} catch (IOException e) {
e.printStackTrace();
}
}
}
OUTPUT
encodedBytes KEBAQEAmJiYmJik=
decodedBytes (####&&&&&)
Related
I am using a CryptoJS(AES) for encryption in Angular 4 using below code:
const key = CryptoJS.enc.Utf8.parse('7061737323313233');
const iv = CryptoJS.enc.Utf8.parse('7061737323313233');
const encrypted = CryptoJS.AES.encrypt('String to encrypt', key, {
keySize: 16,
iv: iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log('Encrypted :' + encrypted);
and below java code to decrypt using AES:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Encryption {
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(String myKey)
{
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String encrypt(String strToEncrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
public static String decrypt(String strToDecrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
}
and
public class Test {
public static void main(String[] args) {
final String secretKey = "7061737323313233";
String originalString = "Response from angular";
String decryptedString = Encryption.decrypt(originalString, secretKey);
System.out.println(decryptedString);
}
}
Angular and java both code works fine if i run it independently for Encryption and Decryption. but when i encrypt using angular and decrypt using java it gives error:
Error while decrypting: javax.crypto.BadPaddingException: Given final block not properly padded
Now my issues are there is a diffrence of padding in angular and java. In angular it is Pkcs7 and in java it is Pkcs5 but this link Padding
says both are same then, why this error is comming. Please help me
My code to generate key was generating bad key in java. So i changed it to :
Key key = new SecretKeySpec(key.getBytes("UTF-8"),"AES" );
Now its working fine
This is a little problematic because of the difference in Javascript and Java libraries.
Please follow the below code which worked for me and I was successfully able to encrypt the data in Java and decrypt in Java.
import java.security.Key;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Crypt {
private static final String ALGO = "AES"; // Default uses ECB PKCS5Padding
public static String encrypt(String Data, String secret) throws Exception {
Key key = generateKey(secret);
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = Base64.getEncoder().encodeToString(encVal);
return encryptedValue;
}
public static String decrypt(String strToDecrypt, String secret) {
try {
Key key = generateKey(secret);
Cipher cipher = Cipher.getInstance(ALGO);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
private static Key generateKey(String secret) throws Exception {
byte[] decoded = Base64.getDecoder().decode(secret.getBytes());
Key key = new SecretKeySpec(decoded, ALGO);
return key;
}
public static String decodeKey(String str) {
byte[] decoded = Base64.getDecoder().decode(str.getBytes());
return new String(decoded);
}
public static String encodeKey(String str) {
byte[] encoded = Base64.getEncoder().encode(str.getBytes());
return new String(encoded);
}
public static void main(String a[]) throws Exception {
/*
* Secret Key must be in the form of 16 byte like,
*
* private static final byte[] secretKey = new byte[] { ‘m’, ‘u’, ‘s’, ‘t’, ‘b’,
* ‘e’, ‘1’, ‘6’, ‘b’, ‘y’, ‘t’,’e’, ‘s’, ‘k’, ‘e’, ‘y’};
*
* below is the direct 16byte string we can use
*/
String secretKey = "mustbe16byteskey";
String encodedBase64Key = encodeKey(secretKey);
System.out.println("EncodedBase64Key = " + encodedBase64Key); // This need to be share between client and server
// To check actual key from encoded base 64 secretKey
// String toDecodeBase64Key = decodeKey(encodedBase64Key);
// System.out.println("toDecodeBase64Key = "+toDecodeBase64Key);
String toEncrypt = "Please encrypt this message!";
System.out.println("Plain text = " + toEncrypt);
// AES Encryption based on above secretKey
String encrStr = Crypt.encrypt(toEncrypt, encodedBase64Key);
System.out.println("Cipher Text: Encryption of str = " + encrStr);
// AES Decryption based on above secretKey
String decrStr = Crypt.decrypt(encrStr, encodedBase64Key);
System.out.println("Decryption of str = " + decrStr);
}
}
<!DOCTYPE html>
<html>
<body>
<h2>Java JavaScript Encryption & Decryption</h2>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script type="text/javascript">
console.log('crypto - js’, CryptoJS);
var encryptedBase64Key = 'bXVzdGJlMTZieXRlc2tleQ==’;
var parsedBase64Key = CryptoJS.enc.Base64.parse(encryptedBase64Key);
var encryptedData = null;
{
// Encryption process
var plaintText = "Please encrypt this message!”;
// console.log( "plaintText = " + plaintText );
// this is Base64-encoded encrypted data
encryptedData = CryptoJS.AES.encrypt(plaintText, parsedBase64Key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log("encryptedData = " + encryptedData);
}
{
// Decryption process
var encryptedCipherText = 'U2WvSc8oTur1KkrB6VGNDmA3XxJb9cC+T9RnqT4kD90=’; // or encryptedData;
var decryptedData = CryptoJS.AES.decrypt(encryptedCipherText, parsedBase64Key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
// console.log( "DecryptedData = " + decryptedData );
// this is the decrypted data as a string
var decryptedText = decryptedData.toString(CryptoJS.enc.Utf8);
console.log("DecryptedText = " + decryptedText);
}
</script>
</body>
</html>
I've written code that generates the key pairs, but was wondering if there's any way to save and re-use them?
Here is the code that generaes the pair:
public static void main(String[] args) throws Exception {
String plainText = "Hello world";
Map<String, Object> keys = getRSAKeys();
PrivateKey privateKey = (PrivateKey) keys.get("private");
PublicKey publicKey = (PublicKey) keys.get("public");
System.out.println(privateKey.getEncoded());
System.out.println(publicKey.getEncoded());
String encrypted = encryptMessage(plainText, privateKey);
System.out.println(encrypted);
String decrypted = decryptMessage(plainText, publicKey, encrypted);
System.out.println(decrypted);
}
private static Map<String, Object> getRSAKeys() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Map<String, Object> keys = new HashMap<String, Object>();
keys.put("private", privateKey);
keys.put("public", publicKey);
return keys;
}
https://docs.oracle.com/javase/tutorial/security/apisign/step2.html -- good entry point.
Also here is some example code to do exactly what you want:
package mx.playground.security;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class AppForStackOverflow {
public static final int KEY_SIZE = 2048;
public static final String PUBLIC_KEY_X509 = "C:\\workspace\\rsa-pair\\public-key";
public static final String PUBLIC_KEY_PKCS1 = "C:\\workspace\\rsa-pair\\public-key-pkcs1";
public static final String PUBLIC_KEY_PEM = "C:\\workspace\\rsa-pair\\public-key-pem";
public static final String PRIVATE_KEY_PKCS8 = "C:\\workspace\\rsa-pair\\private-key";
public static final String PRIVATE_KEY_PKCS1 = "C:\\workspace\\rsa-pair\\private-key-pkcs1";
public static final String PRIVATE_KEY_PEM = "C:\\workspace\\rsa-pair\\private-key-pem";
public static final String SIGNATURE_PATH = "C:\\workspace\\rsa-pair\\signature";
public static final String PRIVATE_KEY_PATH = PRIVATE_KEY_PKCS8;
public static final String PUBLIC_KEY_PATH = PUBLIC_KEY_X509;
public static void main(String[] args) {
generateRsaKeysPair();
encryptDecryptTest();
// symmetric encryption example, use it to store your Private Key in safe manner
String message = "test message";
String rightPass = "0123456789ABCDEF"; // for AES password should be at least 16 chars
String wrongPass = "zzz";
byte[] encryptedMessage = symmetricEncrypt(message.getBytes(), rightPass);
System.out.print(new String(encryptedMessage));
byte[] decryptedMessage = symmetricDecrypt(encryptedMessage, rightPass);
System.out.println(new String(decryptedMessage));
}
public static void generateRsaKeysPair() {
try {
KeyPairGeneratorJdk kpg = new KeyPairGeneratorJdk(KEY_SIZE, "RSA");
PublicKey publicKey = kpg.getPublicKey();
PrivateKey privateKey = kpg.getPrivateKey();
save(PUBLIC_KEY_PATH, publicKey.getEncoded());
save(PRIVATE_KEY_PATH, privateKey.getEncoded());
} catch (Exception e) {
throw new RuntimeException("Failed to execute generateRsaKeysPair()", e);
}
}
public static void encryptDecryptTest() {
try {
byte[] privateKeyBytes = read(PRIVATE_KEY_PATH);
byte[] publicKeyBytes = read(PUBLIC_KEY_PATH);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = kf.generatePrivate(privateKeySpec);
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = kf.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA");
// doing encryption
String message = "test message";
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encodedMessage = cipher.doFinal(message.getBytes("UTF-8"));
System.out.println("ENCRYPTED: " + new String(encodedMessage));
// doing decryption
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodedMessage = cipher.doFinal(encodedMessage);
System.out.println("DECRYPTED: " + new String(decodedMessage));
} catch (Exception e) {
throw new RuntimeException("Failed to execute encryptDecryptTest()", e);
}
}
private static void save(String path, byte[] data) {
try {
File file = new File(path);
file.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(file)){
fos.write(Base64.getEncoder().encode(data));
fos.flush();
};
} catch (IOException e) {
throw new RuntimeException("Failed to save data to file: " + path, e);
}
}
private static byte[] read(String path) {
try {
return Base64.getDecoder().decode(Files.readAllBytes(new File(path).toPath()));
} catch (IOException e) {
throw new RuntimeException("Failed to read data from file: " + path, e);
}
}
/*
* Use this to encrypt your private key before saving it to disk
*/
public static byte[] symmetricEncrypt(byte[] data, String password) {
try {
SecretKeySpec secretKey = new SecretKeySpec(password.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] result = cipher.doFinal(data);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to execute symmetricEncrypt()", e);
}
}
public static byte[] symmetricDecrypt(byte[] data, String password) {
try {
SecretKeySpec secretKey = new SecretKeySpec(password.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] result = cipher.doFinal(data);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to execute symmetricEncrypt()", e);
}
}
}
I have this code in my rails app:
require 'openssl'
require 'digest/sha1'
require 'base64'
KEY="secret_key"
data = "secret message"
def encrypt_value(data)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.encrypt
cipher.key = Digest::SHA256.digest(KEY)
encrypted = cipher.update(data)+cipher.final
return encrypted
end
def decrypt_value1(data)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.key = Digest::SHA256.digest(KEY)
decrypted = cipher.update(data)+cipher.final
data = Base64.decode64(decrypted)
return data
end
And java code:
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class EncryptionDecryption {
private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;
// private static SecretKey secretKey;
private static final byte[] secretKey = "secret_key".getBytes();
public static void main(String []args) throws Exception {
salt = getSalt();
char[] message = "secret message".toCharArray();
System.out.println("Message: " + String.valueOf(message));
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}
public static String encrypt(char[] plaintext) throws Exception {
byte[] saltBytes = salt.getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
//secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrypt(char[] encryptedText) throws Exception {
System.out.println(encryptedText);
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
SecretKeySpec secretSpec = new SecretKeySpec(secretKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return new String(salt);
}
}
How can I have both of them work with each other, for example if I send an encrypted data to java app from rails app it should be able to decode it and vice-versa.
As far as you are using the same cypher algorithms and message padding characters, they should work just fine.
Also, it is better to use some key exchange protocol, like Diffie-Hellman. Both Ruby's OpenSSL wrapper and Java's Crypto lib support it. It is a safer way to generate per session keys.
PS. Your code as you have it, seems to be written for a standalone execution. For example, your Java code for decoding assumes that your string was encoded with the same instance's encode method. Because, your decode method is using IV that was initialized in encode method (ivBytes).
I am encrypting the string using AES but it is not decrypting "1234567812345678" character back to plain text from encrypted string. For other text (like "Hello World") it is working fine. Also want to keep the encrypted code to accept only UTF-8 characters. I am using below code
import java.security.*;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class StrongAES
{
public static void main(String[] args)
{
//listing all available cryptographic algorithms
/* for (Provider provider: Security.getProviders()) {
System.out.println(provider.getName());
for (String key: provider.stringPropertyNames())
System.out.println("\t" + key + "\t" + provider.getProperty(key));
}*/
StrongAES saes = new StrongAES();
String encrypt = saes.encrypt(new String("Bar12346Bar12346"),new String("1234567812345678"));
//String encrypt = saes.encrypt(new String("Bar12346Bar12346"),new String("Hello world"));
System.out.println(encrypt);
String decrypt = saes.decrypt(new String("Bar12346Bar12346"), new String(encrypt));
System.out.println(decrypt);
}
String encrypt(String key, String text)
{
String encryptedText="";
try{
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
encryptedText = new String(encrypted);
// System.out.println(encryptedText);
}
catch(Exception e)
{
e.printStackTrace();
}
return encryptedText;
}
String decrypt(String key, String encryptedText)
{
String decryptedText="";
try{
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
decryptedText = new String(cipher.doFinal(encryptedText.getBytes()));
// System.out.println("Decrypted "+decryptedText);
}
catch(Exception e)
{
e.printStackTrace();
}
return decryptedText;
}
}
You must keep bytes, not converting to String.
explanations in this post:
Problems converting byte array to string and back to byte array
If you want a keep a String, think of convert to B64
String my_string=DatatypeConverter.printBase64Binary(byte_array);
and
byte [] byteArray=DatatypeConverter.parseBase64Binary(my_string);
you can also convert to Hexa (it's bigger)
I have chenged my code to below. Used BASE64Encoder to convert incrypted byte to string same converted with BASE64Decoder while decrypting. And UTF-8 for each getByte.
import java.security.*;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class StrongAES
{
public static void main(String[] args)
{
StrongAES saes = new StrongAES();
String encrypt = saes.encrypt(new String("Bar12346Bar12346"),new String("1234567812345678"));
System.out.println(encrypt);
String decrypt = saes.decrypt(new String("Bar12346Bar12346"), new String(encrypt));
System.out.println(decrypt);
}
String encrypt(String key, String text)
{
String encryptedText="";
try{
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes("utf-8"), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes("utf-8"));
BASE64Encoder encoder = new BASE64Encoder();
encryptedText = encoder.encodeBuffer(encrypted);
}
catch(Exception e)
{
e.printStackTrace();
}
return encryptedText;
}
String decrypt(String key, String encryptedText)
{
String decryptedText="";
try{
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes("utf-8"), "AES");
Cipher cipher = Cipher.getInstance("AES");
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
BASE64Decoder decoder = new BASE64Decoder();
decryptedText = new String(cipher.doFinal(decoder.decodeBuffer(encryptedText)));
}
catch(Exception e)
{
e.printStackTrace();
}
return decryptedText;
}
}
I found a guide for implementing AES encryption/decryption in Java and tried to understand each line as I put it into my own solution. However, I don't fully understand it and am having issues as a result. The end goal is to have passphrase based encryption/decryption. I've read other articles/stackoverflow posts about this, but most do not provide enough explanation (I am very new to crypto in Java)
My main issues right now are that even when I set byte[] saltBytes = "Hello".getBytes();
I still get a different Base64 result in the end (char[] password is random each time, but I read that it is safer to leave passwords in char[] form. My other problem is that when the program gets to decrypt(), I get a NullPointerException at
byte[] saltBytes = salt.getBytes("UTF-8");
Thank you in advance for any help/advice you can give me.
The code in question:
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class EncryptionDecryption {
private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;
public static void main(String []args) throws Exception {
char[] message = "PasswordToEncrypt".toCharArray();
System.out.println("Message: " + message.toString());
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}
public static String encrypt(char[] plaintext) throws Exception {
salt = getSalt();
byte[] saltBytes = salt.getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plaintext.toString().getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrypt(char[] encryptedText) throws Exception {
byte[] saltBytes = salt.getBytes("UTF-8");
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(encryptedText.toString());
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(encryptedText, saltBytes, iterations, keySize);
SecretKey secretkey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretkey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return decryptedTextBytes.toString();
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return salt.toString();
}
}
I think that you are making two mistakes :)
I've corrected your sample code to make it work :
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class EncryptionDecryption {
private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;
private static SecretKey secretKey;
public static void main(String []args) throws Exception {
salt = getSalt();
char[] message = "PasswordToEncrypt".toCharArray();
System.out.println("Message: " + String.valueOf(message));
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}
public static String encrypt(char[] plaintext) throws Exception {
byte[] saltBytes = salt.getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrypt(char[] encryptedText) throws Exception {
System.out.println(encryptedText);
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return new String(salt);
}
}
The first mistake is that you generate 2 different salts (when using the encrypt method), so encrypted/decrypted logs were differents (logical, but the decryption would still work because you are calling the decryption directly after encryption).
The second mistake was for the secret key. You need to generate a secret key when you are encrypting, but not decrypting. To put it more simply, it is as if i was encrypting with the password "encrypt" and that you are trying to decrypt it with the password "decrypt".
I would advise you to generate every random stuff (such as private key, salt etc on startup). But beware that when you'll stop your app, you won't be able to decrypt old stuff unless getting the exact same random stuff.
Hope I helped :)
Regards,