What I need is to encrypt string which will show up in 2D barcode(PDF-417) so when someone get an idea to scan it will get nothing readable.
Other requirements:
public static String encryptParams(String myTextInput) {
String myKey = "40674244454045cb9a70040a30e1c007";
String myVector = "@1B2c3D4e5F6g7H8";
String encData = "";
try{
JavaEncryprtionUtil encUtil = new JavaEncryprtionUtil();
encData = Base64.encodeToString(encUtil.encrypt(myTextInput.getBytes("UTF-8"), myKey.getBytes("UTF-8"), myVector.getBytes("UTF-8")),Base64.DEFAULT);
System.out.println(encData);
}catch(NoSuchAlgorithmException ex){
ex.printStackTrace();
}catch(NoSuchPaddingException ex){
ex.printStackTrace();
}catch(InvalidKeyException ex){
ex.printStackTrace();
}catch(InvalidAlgorithmParameterException ex){
ex.printStackTrace();
}catch(IllegalBlockSizeException ex){
ex.printStackTrace();
}catch(BadPaddingException ex){
ex.printStackTrace();
}catch(UnsupportedEncodingException ex){
ex.printStackTrace();
}
return encData;
}
Here is a copy/paste solution. I also recommend reading and voting for @Konstantino's answer even though it doesn't provider any code. The initialization vector (IV) is like a salt - it doesn't have to be kept secret. I am new to GCM and apparently AAD is optional and only used in certain circumstances. Set the key in the environment variable SECRET_KEY_BASE
. Use something like KeePass to generate a 32 character password. This solution is modeled after my Ruby solution.
public static String encrypt(String s) {
try {
byte[] input = s.getBytes("UTF-8");
String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
if (keyString == null || keyString.length() == 0) {
Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
return null;
}
byte[] keyBytes = keyString.getBytes("UTF-8");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
// generate IV
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] ivBytes = new byte[cipher.getBlockSize()];
secureRandom.nextBytes(ivBytes);
GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes); // 96 bit tag length
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
// generate AAD
// byte[] aadBytes = new byte[cipher.getBlockSize()];
// secureRandom.nextBytes(aadBytes);
// cipher.updateAAD(aadBytes);
// encrypt
byte[] encrypted = cipher.doFinal(input);
byte[] returnBytes = new byte[ivBytes.length + encrypted.length];
// byte[] returnBytes = new byte[ivBytes.length + aadBytes.length + encrypted.length];
System.arraycopy(ivBytes, 0, returnBytes, 0, ivBytes.length);
// System.arraycopy(aadBytes, 0, returnBytes, ivBytes.length, aadBytes.length);
System.arraycopy(encrypted, 0, returnBytes, ivBytes.length, encrypted.length);
// System.arraycopy(encrypted, 0, returnBytes, ivBytes.length+aadBytes.length, encrypted.length);
String encryptedString = Base64.getEncoder().encodeToString(returnBytes);
return encryptedString;
} catch (UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
Logger.error(Utils.class, "encrypt()", "Could not encrypt string: " + e.getMessage());
return null;
}
}
public static String decrypt(String s) {
if (s == null || s.length() == 0) return "";
try {
byte[] encrypted = Base64.getDecoder().decode(s);
String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
if (keyString == null || keyString.length() == 0) {
Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
return null;
}
byte[] keyBytes = keyString.getBytes("UTF-8");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] ivBytes = new byte[cipher.getBlockSize()];
System.arraycopy(encrypted, 0, ivBytes, 0, ivBytes.length);
GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
// cipher.updateAAD(encrypted, ivBytes.length, cipher.getBlockSize());
byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize(), encrypted.length - cipher.getBlockSize());
// byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize()*2, encrypted.length - cipher.getBlockSize()*2);
String decryptedString = new String(decrypted, "UTF-8");
return decryptedString;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedEncodingException | InvalidKeyException |
InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
Logger.error(Utils.class, "decrypt()", "Could not decrypt string: " + e.getMessage());
return null;
}
}
Here is an example:
String s = "This is a test.";
String enc = Utils.encrypt(s);
System.out.println(enc);
// fQHfYjbD+xAuN5XzH2ojk/EWNeKXUrKRSfx8LU+5dpuKkM/pueCMBjKCZw==
String dec = Utils.decrypt(enc);
System.out.println(dec);
// This is a test.
Here's my implementation from meta64.com as a Spring Singleton. If you want to create a ciper instance for each call that would work also, and then you could remove the 'synchronized' calls, but beware 'cipher' is not thread-safe.
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class Encryptor {
@Value("${aeskey}")
private String keyStr;
private Key aesKey = null;
private Cipher cipher = null;
synchronized private void init() throws Exception {
if (keyStr == null || keyStr.length() != 16) {
throw new Exception("bad aes key configured");
}
if (aesKey == null) {
aesKey = new SecretKeySpec(keyStr.getBytes(), "AES");
cipher = Cipher.getInstance("AES");
}
}
synchronized public String encrypt(String text) throws Exception {
init();
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
return toHexString(cipher.doFinal(text.getBytes()));
}
synchronized public String decrypt(String text) throws Exception {
init();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
return new String(cipher.doFinal(toByteArray(text)));
}
public static String toHexString(byte[] array) {
return DatatypeConverter.printHexBinary(array);
}
public static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
/*
* DO NOT DELETE
*
* Use this commented code if you don't like using DatatypeConverter dependency
*/
// public static String toHexStringOld(byte[] bytes) {
// StringBuilder sb = new StringBuilder();
// for (byte b : bytes) {
// sb.append(String.format("%02X", b));
// }
// return sb.toString();
// }
//
// public static byte[] toByteArrayOld(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;
// }
}
I would consider using something like https://www.bouncycastle.org/ It is a prebuilt library that allows you to encrypt whatever you like with a number of different Ciphers I understand that you only want to protect from snooping, but if you really want to protect the information, using Base64 won't actually protect you.