I'm working on an embedded application, and I need to decrypt a message using PBEKeySpec, since the password is a hash, I don't know if the algorithm is correct, because I always have a return similar to this: '?s.PH ?~+? ?*ol XL >?? ;#??????'
public static String decrypt(String encrypted) throws Exception {
byte[] original = null;
try {
char[] pw = getSecret().toCharArray();
byte[] salt = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
PBEKeySpec pbeKeySpec = new PBEKeySpec(pw, salt, 1000, 384);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
original = cipher.doFinal(encrypted.getBytes());
} catch (Exception ex) {
ex.printStackTrace();
}
return new String(original, StandardCharsets.US_ASCII);
}
To generate the password hash:
private static String getSecret() {
StringBuffer hexString = new StringBuffer();
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(Arrays.copyOf(secret.getBytes(), 36));
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
} catch (Exception ex) {
Logger.getLogger(CriptografiaHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return hexString.toString();
}
Related
I want to decrypt a String. Here my Decryption and Encryption methods.
public String encrypt(String message) throws Exception {
byte[] messageInBytes = message.getBytes();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = encryptionCipher.doFinal(messageInBytes);
return encode(encryptedBytes);
}
public String decrypt(String encryptedMessage) throws Exception {
byte[] messageInBytes = decode(encryptedMessage);
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(T_LEN , encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decryptedBytes = decryptionCipher.doFinal(messageInBytes);
return new String(decryptedBytes);
}
Here the main:
public static void main(String[] args) {
try {
AES aes = new AES();
aes.convertStringKeyToSecretKey();
String encryptedMessage = aes.encrypt("Peter");
String decryptedMessage = aes.decrypt(encryptedMessage);
System.err.println("Encrypted Message : " + encryptedMessage);
System.err.println("Decrypted Message : " + decryptedMessage);
} catch (Exception ignored) {
}
}
When I change encryptedMessage to a own String like:
String decryptedMessage = aes.decrypt("xDFzl9HsenqKspdEbL/m9I5X6dqn");
It does nothing
I hope you can help me.
Best Regards
Christian
public static String encryptAES(String toEncrypt, final String key1, final String key2) throws Exception {
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(key1.toCharArray(), key2.getBytes(), 65536, 256);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(toEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception ex) {
throw new Exception(ex);
}
}
public static String decryptAES(String toDecrypt, final String key1, final String key2) throws Exception {
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(key1.toCharArray(), key2.getBytes(), 65536, 256);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(toDecrypt)));
} catch (Exception ex) {
throw new Exception(ex);
}
}
Here you can still expand its secureness by creating your own IV Spec key, which should contain only 16 character.
Actually, this is how AES encryption worked for me, You can also check this repo in GitHub for additional encryption methods.
I encrypt data in android phone using AES GCM mode and sent it to java application in windows
The cipher text is created and received successfully
at decryption process an exception appeared(Tag miss match)
I tried to remove associated data update
I tried to set the provider to SunJCE
The code works great between java desktop applications but the error appears when use encryption decryption process between android and java
I use openjdk1.8
public static byte[] gcmMode(int gcmCipherMode, byte[] aadHeader,char[] password, byte[] inputBytes) {
byte[] outputBytes = new byte[0];
byte[] salt;
byte[] iv;
byte[] res;
SecretKeySpec ks;
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
switch (gcmCipherMode) {
case Cipher.ENCRYPT_MODE:
salt = new byte[16]; random.nextBytes(salt);
iv = new byte[12]; random.nextBytes(iv);
res = salt(password, salt);
ks = new SecretKeySpec(res, "AES");
c.init(Cipher.ENCRYPT_MODE, ks, new GCMParameterSpec(128, iv));
if (aadHeader != null) {
c.updateAAD(aadHeader);
}
outputBytes = arrayByteConcatenate(salt, iv);
outputBytes = arrayByteConcatenate(outputBytes, c.doFinal(inputBytes));
break;
case Cipher.DECRYPT_MODE:
if (inputBytes.length > 44) {
salt = arrayByteSplit(inputBytes, 0, 16);
iv = arrayByteSplit(inputBytes, 16, 28);
res = salt(password, salt);
ks = new SecretKeySpec(res, "AES");
c.init(Cipher.DECRYPT_MODE, ks, new GCMParameterSpec(128, iv));
if (aadHeader != null) {
c.updateAAD(aadHeader);
}
// Return our Decrypted String
outputBytes = c.doFinal(arrayByteSplit(inputBytes, 28, inputBytes.length));
}else{
System.out.println("Wrong cipher");
}
break;
default:
System.out.println("UnKnown");
break;
}
} catch (NoSuchAlgorithmException | NoSuchPaddingException
| InvalidKeyException | IllegalBlockSizeException
| BadPaddingException | InvalidParameterException
| InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return outputBytes;
}
static byte[] salt (char[] password, byte[] salt) {
SecretKeyFactory skf = null;
byte[] res = new byte[0];
try {
skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password, salt, 100000, 128);
SecretKey key = skf.generateSecret(spec);
res = key.getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
return res;
}
Error
javax.crypto.AEADBadTagException: Tag mismatch!
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:578)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at algorithm.encryption.AESencryption.gcmMode(AESencryption.java:91)
at ServerWorker.handleAuthentication(ServerWorker.java:97)
at ServerWorker.startMessageReader(ServerWorker.java:67)
at ServerWorker.handleClientSocket(ServerWorker.java:44)
at ServerWorker.run(ServerWorker.java:36)
I am getting following error while decrypting.
Once it was working fine but suddenly I am getting this error.
How can I solve this problem?
I have read many articles but couldn't get help.
java.security.InvalidKeyException: Parameters missing
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:388)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:186)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:848)
at javax.crypto.Cipher.init(Cipher.java:1212)
at javax.crypto.Cipher.init(Cipher.java:1152)
at
com.test.security.TestEncryptDecrypt.decrypt(TestEncryptDecrypt.java:92)
at com.test.security.TestEncryptDecrypt.main(TestEncryptDecrypt.java:59)
The code is give below:
public static void main(String []args){
try {
String encString = encrypt("PID=0000000003|ITC=NA|PRN=MNKB0701511135|AMT=1.00|CRN=INR|RU=https://www.testsite.com/testsk/servlet/TestResponseHandler?");
System.out.println("Enc : " + encString);
System.out.println("Dec : "+ decrypt(encString));
} catch (Exception e) {
e.printStackTrace();
}
}
Encrypt method
public static String encrypt(String data) throws Exception {
String keyFile = "E:\\testpath\\Keys\\0000000003.key";
byte[] keyb = Files.readAllBytes(Paths.get(keyFile));
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, skey);
byte[] encVal = c.doFinal(data.getBytes());
return new BASE64Encoder().encode(encVal);
}
Decrypt method
public static String decrypt(String encryptedData)
throws InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, IOException {
String keyFile = "E:\\testpath\\Keys\\0000000003.key";
byte[] keyb = Files.readAllBytes(Paths.get(keyFile));
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, skey);
byte[] decordedValue = Base64.decodeBase64(encryptedData.getBytes());
byte[] decValue = c.doFinal(decordedValue);
return new String(decValue);
}
As the error message says, you are missing a parameter in the init methods. If you're using CBC you should also specify the AlgorithmParameterSpec.
Could you perhaps try the following:
byte[] paramSpecBytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec paramSpec = new IvParameterSpec(paramSpecBytes);
And in your encrypt and decrypt methods:
c.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
c.init(Cipher.DECRYPT_MODE, skey, paramSpec);
You could fill the bytes with any value you like though, doesn't need to be zero's. Best would be some random value.
I am trying to write an encryption/decryption utility class, but not matter what I do I cannot seem to get decryption working. I keep getting a javax.crypto.BadPaddingException: Given final block not properly padded exception during decryption.
I've looked at a number of examples and other stack overflow questions but can't seem to find my mistake
public class EncryptionUtil {
private static final Log LOGGER = LogFactory.getLog(EncryptionUtil.class);
private static final String CIPHER_MODE = "AES/CBC/PKCS5PADDING";
private static final String CRYPTO_PROPERTIES_PATH = "/crypto.properties";
private static final SecretKeySpec sKey = keySpecFromProperties();
private EncryptionUtil() {}
public static byte[] encrypt(byte[] aBytes) {
try {
SecureRandom lSecureRandom = new SecureRandom();
byte[] ivBytes = new byte[16];
lSecureRandom.nextBytes(ivBytes);
IvParameterSpec lSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
cipher.init(Cipher.ENCRYPT_MODE, sKey, lSpec);
byte[] encryptedBytes = cipher.doFinal(aBytes);
byte[] outBytes = new byte[encryptedBytes.length + 16];
System.arraycopy(ivBytes, 0, outBytes, 0, 16);
System.arraycopy(encryptedBytes, 0, outBytes, 16, encryptedBytes.length);
return outBytes;
} catch (Exception aEx) {
LOGGER.error("Failed to encrypt bytes");
throw new RuntimeException(aEx);
}
}
public static byte[] decrypt(byte[] aBytes) {
try {
byte[] lIvBytes = Arrays.copyOfRange(aBytes, aBytes.length - 16, aBytes.length);
byte[] lEncryptedBytes = Arrays.copyOfRange(aBytes, 0, aBytes.length - 16);
IvParameterSpec lIvSpec = new IvParameterSpec(lIvBytes);
Cipher cipher = Cipher.getInstance(CIPHER_MODE);
cipher.init(Cipher.DECRYPT_MODE, sKey, lIvSpec);
return cipher.doFinal(lEncryptedBytes);
}catch (Exception aEx){
LOGGER.error("Failed to decrypt bytes. Returning input bytes", aEx);
return aBytes;
}
}
private static SecretKeySpec keySpecFromProperties(){
try(InputStream lPropStream = EncryptionUtil.class.getResourceAsStream(CRYPTO_PROPERTIES_PATH)){
Properties cryptoProps = new Properties();
cryptoProps.load(lPropStream);
String lSecret = cryptoProps.getProperty("secret");
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(lSecret.getBytes("UTF-8"));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(),0, keyBytes, 0, keyBytes.length);
return new SecretKeySpec(keyBytes, "AES");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
You prepend your IV to the ciphertext on encryption, but on decryption you copy the last 16 bytes as your IV.
Whatever you do on encryption you must undo on decryption.
I have a Java AES 256 encryption code like this :
public class EncryptDecryptTwo {
static {
try {
Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
field.setAccessible(true);
field.set(null, java.lang.Boolean.FALSE);
} catch (Exception ex) {
}
}
private static String keyEnc = "BtDMQ7RfNVoRzJ7GYE32";
// Performs Encryption
public static String encrypt(String plainText) throws Exception {
String passphrase = keyEnc;
byte[] iv = DatatypeConverter.parseHexBinary("2aba86027a6f79dd463b81b0539bacb5");
byte[] salt = DatatypeConverter.parseHexBinary("0f3d1b0d514ca313");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeySpec sKey = (SecretKeySpec) generateKeyFromPassword(passphrase, salt);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, sKey, ivParameterSpec);
byte[] encryptedData = c.doFinal(plainText.getBytes());
return DatatypeConverter.printBase64Binary(encryptedData);
}
public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 1, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(secretKey.getEncoded(), "AES");
}
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;
}
}
In this case, I hardcoded the salt bytes and iv bytes just to make sure my coding is encrypt the message right, so it can be read by cryptoJS in another server. But when I send the encrypted text by calling the encrypt method, it is always rejected by the cryptoJS, saying that my encrypted message is not correct
The CryptoJS is in another server and it is like this :
var CryptoJSAesJson = {
stringify: function (cipherParams) {
var j = {ct: cipherParams.cipherText.toString(CryptoJS.enc.Base64)};
if (cipherParams.iv) j.iv = cipherParams.iv.toString();
if (cipherParams.salt) j.s = cipherParams.salt.toString();
return JSON.stringify(j);
},
parse: function (jsonStr) {
var j = JSON.parse(jsonStr);
var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Base64.parse(j.ct)});
if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv)
if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s)
return cipherParams;
}
}
var encrypted = CryptoJS.AES.encrypt(JSON.stringify($scope.loginData.password), __ENV.AES_KEY, { format: CryptoJSAesJson}).toString();
Can anybody help me finding what's wrong in my java code? Thank you very much
Edited :
According to Artjom.B's comment and advise, I tried this :
static {
try {
Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
field.setAccessible(true);
field.set(null, java.lang.Boolean.FALSE);
} catch (Exception ex) {
}
}
public static void main(String[] args) throws UnsupportedEncodingException, GeneralSecurityException {
String plaintext = "someplaintext";
String password = "BtDMQ7RfNVoRzWGjS2DK";
int keySize = 256;
int ivSize = 128;
byte[] salt = DatatypeConverter.parseHexBinary("5ba2b0e0bb968f47"); // yes, for testing, I use a fixed salt
byte[] key = new byte[keySize/8];
byte[] iv = new byte[ivSize/8];
EvpKDF(password.getBytes("UTF-8"), keySize, ivSize, salt, key, iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
byte[] encryptedText = cipher.doFinal((plaintext).getBytes());
System.out.println("SALT : " + DatatypeConverter.printHexBinary(salt));
System.out.println("IV : " + DatatypeConverter.printHexBinary(iv));
System.out.println("CT : " + DatatypeConverter.printBase64Binary(encryptedText));
// I sent salt, IV, and ciphertext to CryptoJS
}
public static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
return EvpKDF(password, keySize, ivSize, salt, 1, "MD5", resultKey, resultIv);
}
public static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, int iterations, String hashAlgorithm, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
keySize = keySize / 32;
ivSize = ivSize / 32;
int targetKeySize = keySize + ivSize;
byte[] derivedBytes = new byte[targetKeySize * 4];
int numberOfDerivedWords = 0;
byte[] block = null;
MessageDigest hasher = MessageDigest.getInstance(hashAlgorithm);
while (numberOfDerivedWords < targetKeySize) {
if (block != null) {
hasher.update(block);
}
hasher.update(password);
block = hasher.digest(salt);
hasher.reset();
// Iterations
for (int i = 1; i < iterations; i++) {
block = hasher.digest(block);
hasher.reset();
}
System.arraycopy(block, 0, derivedBytes, numberOfDerivedWords * 4,
Math.min(block.length, (targetKeySize - numberOfDerivedWords) * 4));
numberOfDerivedWords += block.length/4;
}
System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4);
System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4);
return derivedBytes; // key + iv
}
/**
* Copied from http://stackoverflow.com/a/140861
* */
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;
}
But still no avail. The CryptoJS keep saying that I pass an incorrect password.
Can anybody help? Thank you very much