According to section 5.2 (Two GCM Functions) of the Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC, it mentions that for the case of GMAC, the authenticated encryption and decryption functions become the functions for generating and verifying an authentication tag on the non-confidential data.
Looking online and on the JCA reference page, I understand that GMAC is generated like this:
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();
However, I have doubts regarding the GMAC verification. Is this the right way of verifying a GMAC?
Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();
Where verifiedGmac size == 0?
Here is the full code with inputs and outputs:
import org.hamcrest.core.Is;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static javax.xml.bind.DatatypeConverter.parseHexBinary;
import static javax.xml.bind.DatatypeConverter.printHexBinary;
import static org.junit.Assert.assertThat;
public class TestGmac {
#Test
public void generateAndVerifyGmac() throws Exception {
final byte[] message = parseHexBinary("AAAAAAAAAAAA");
final byte[] authenticatedKey = parseHexBinary("63509E5A672C092CAD0B1DC6CE009A61");
final byte[] aadData = buildAadForAuthenticationOnly(message, authenticatedKey);
final byte[] iv = parseHexBinary("BBBBBBBBBBBBBBBBBBBBBBBB");
SecretKeySpec encryptionKey = new SecretKeySpec(parseHexBinary("55804F3AEB4E914DC91255944A1F565A"), "AES");
// Generate GMAC
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();
assertThat(printHexBinary(gmac), Is.is("44C955D63799428524E97993"));
// Verify GMAC
Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();
assertThat(printHexBinary(verifiedGmac), Is.is(""));
}
private byte[] buildAadForAuthenticationOnly(byte[] message, byte[] authenticatedKey) throws IOException {
ByteArrayOutputStream aaDoutputStream = new ByteArrayOutputStream();
aaDoutputStream.write(parseHexBinary("10"));
aaDoutputStream.write(authenticatedKey);
aaDoutputStream.write(message);
return aaDoutputStream.toByteArray();
}
}
Yes, that is correct. The Java API verifies the MAC, throwing an exception if it fails. It returns the encrypted plaintext message, but in your case the message is empty.
There are two design choices for creating a GCM API: handle the authentication tag separately or assume it is part of the ciphertext. Although I prefer the separate handling of ciphertext and authentication tag, Java/Oracle decided to keep to the AEAD RFC and include the authentication tag with the ciphertext (requiring extra buffering, a code size increase of about 30% and removing the posiblity to perform online decryption).
So although your code maybe feels a bit strange the handing of the MAC seems OK - if it weren't you'd get a AEADBadTagException in the call to doFinal during decryption.
Using a static key and IV certainly isn't OK, but I presume those were used for testing purposes - right?
Related
Java encryption code.
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.List;
import java.util.Map;
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;
import org.json.simple.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jarvis.accountopeningv2.model.PartChanRequestDTO;
import com.jarvis.accountopeningv2.model.TemplateDTO;
public class SecurityService {
private static final String ALGORITHM = "AES/CBC/PKCS5PADDING";
public static String encryptpayload(String value, String key) {
String encryptedData = null;
try {
IvParameterSpec iv = new IvParameterSpec(key.substring(0, 16).getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.substring(0, 32).getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
encryptedData = Base64.getEncoder().encodeToString(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
encryptedData = null;
}
return encryptedData;
}
}
This is what I have tried.
function SecurityFunc(request) {
const digest = 'SHA256';
var secretkey = 'fgbnhgfcjhgfcvjkhgfcvjkhgfcvbjbnvcjhnbvcfghjnbvc'; // 256 character
var iv = Buffer.from(secretkey.substring(0,16), 'utf-8', 'aes');
const key = crypto.pbkdf2Sync(secretkey, 'boooooo!!', 65536, 32, digest);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(`${request}`, 'ascii', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
I have searched a lot about this. I got these questions on stack overflow
Difference in key lengths between crypto.pbkdf2 (node.js) and PBEKeySpec, AES/CBC/PKCS5PADDING IV - Decryption in NodeJs (Encrypted in Java) I have tried both of them but none of them resolved my query.
Any help will be highly appreciated.
Are you trying to make the 2 compatible or just port the code from Java to JavaScript?
Update: apparently PKCS#5 will work the same as for PKCS#7 so that isn't your issue.
The key length should be 32-bytes in both cases.
If you don't need backwards compatibility, I'd recommend using AES in GCM mode rather than CBC as it includes tamper protection.
I know that there are many threads over this , but i am not able to find solution with them.
Problem Statement:
Need to encrypt data. Below is my code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
public class CryptoUtil {
private static final String AES = "AES/ECB/PKCS5Padding";
public String encryptMessage(final String message, final byte[] dataKey) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(dataKey, AES);
try {
Cipher cipher = Cipher.getInstance(AES);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedMessage = cipher.doFinal(message.getBytes());
return Base64.getEncoder().encodeToString(encryptedMessage);
}
catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
throw new Exception("Error while encrypting application credentials", e);
}
}
}
I need to do it using BountyCastle service provider. So i followed the steps mentioned here and this. I am using JAVA 11, but i switched to JAVA 8 to follow mentioned link.
In my code i added static block to add BountyCastle as service provider
static {
BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
Security.addProvider(bouncyCastleProvider);
}
But, it is not getting added as verified providers. So i am getting the issue.
Any idea how to handle. How to add BouncyCastle as verified provider.
I am running on local machine , and need to create JAR.
Nobody has answered this, so I'll just post an answer with the comment I had above. I have not tested it so I leave that part to you.
When you do Cipher.getInstance(), it is correct to have "AES/ECB/PKCS5Padding" (for some weird definition of "correct", where Java providers believe PKCS5 padding = PKCS7 padding, but never mind that).
But when you create the key, it is not correct to have the mode and padding in there. Instead, they just want the algorithm. So try:
SecretKeySpec secretKey = new SecretKeySpec(dataKey, "AES");
The change is the quotes around "AES", rather than using AES="AES/ECB/PKCS5Padding".
Not sure if that will fix your problem, but should be a step in the right direction (I hope).
I am integrating with a .Net application which uses Symmetric encryption. My application is on Java. Following is the .Net code which was shared by the team which uses the Symmetric encryption;
public static string SymmetricEncrypt<T>(string value, string PrivateKey, string SALT_STRING) where T : SymmetricAlgorithm, new()
{
PasswordDeriveBytes rgb =
new PasswordDeriveBytes(PrivateKey ,
Encoding.Unicode.GetBytes(SALT_STRING));
SymmetricAlgorithm algorithm = new T();
byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);
ICryptoTransform transform = algorithm.CreateEncryptor(rgbKey, rgbIV);
using (MemoryStream buffer = new MemoryStream())
{
using (CryptoStream stream =
new CryptoStream(buffer, transform, CryptoStreamMode.Write))
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(value);
}
}
return Convert.ToBase64String(buffer.ToArray());
}
}
Looking at the .Net documentation, I could see that SymmetricEncrypt class by default uses Rijndael encryption. And also I have found a class similar to "PasswordDeriveBytes" implementation in bouncy castle which implements the "PKCS5S1" algorithm for key generation using the salt with an iteration count of 100. But still I am unable to generate the exact encryption to what is required by the .Net application. Following code is what i have tried so far;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import mtnsa.sorb.handler.PKCS5Test.PasswordDeriveBytes;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Base64;
public class EncryptionHelper {
public static void main(String[] args)throws Exception {
encrypt("FTTH","HhN01vcEEtMmwdNFliM8QYg0Y89xzBOJJG7BHARC7g", "002400000480000094000000060200000024000052534131000400000100010085525e9438e9fae122f71ec7124" +
"443bf2f9f57f5f3760b3704df168493004b9ef68413f500d54fa9fa3869b42b1e2365204826e54b618d56e7e575f2" +
"7f675f0eae3ea8458a8ee1e92dc3f4bfc34fbe23851afa9d2c28fc8cd5b124f60a03a06bfb598bc3acbd8c4380ae" +
"f02cc58bdf955d140390f740a7e115c59e3b3b5758ca");
}
public static String encrypt(final String valueToEncrpt, final String saltString, final String privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
String encrptedValue = null;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] password = privateKey.getBytes();
byte[] salt = saltString.getBytes();
PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
generator.init(password, salt, 100);
byte[] keyArr = ((KeyParameter)generator.generateDerivedParameters(128)).getKey();
byte[] IvArr = ((KeyParameter)generator.generateDerivedParameters(128)).getKey();
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyArr,"AES"),new IvParameterSpec(IvArr));
byte[]test = cipher.doFinal(valueToEncrpt.getBytes());
System.out.println(new String(Base64.encode(test)));
return encrptedValue;
}
}
In the Java code i have given the sample SALT key and the PRIVATE key used for testing purposes. But still I am unable to get the exact value as generated by the .Net application. Following is an example plain text and the encrypted value which i got from the .Net application which i have failed to replicate in my Java code.
Plain text value : FTTH
Encrypted value : MjgmbdT3Vg6RW/7K1BjQ/Q==
Any help on this is much appreciated as i am currently out of ideas.
Thank you everyone for the valuable comments. I have requested the .Net module authors to change their implementation to use RFC2898DeriveBytes
I ran into a problem when trying to integrate with a payment processor (CCBill) who is requesting a string to be encrypted to be used as a token, using the TripleDES standard. My application is in Java, and the result I am getting back is not what the payment processor expects.
There is a post here that is addressing Perl<->PHP and Perl<->ColdFusion, but no solutions for Java.
The PERL version of the code looks like this:
#!/usr/bin/perl
# Perl Crypt Calamity (here be...something)
use strict;
use CGI;
use MIME::Base64;
use Crypt::TripleDES;
my $cgi = CGI->new();
my $param = $cgi->Vars();
$param->{key} = "bC5PEcLzvb+jY1FZWuP4pw50";
$param->{string} = "subscriptionId=0214288302000000207";
my $des = Crypt::TripleDES->new();
my $enc = $des->encrypt3($param->{string}, $param->{key});
$enc = encode_base64($enc);
$enc =~ s/\n//gs;
# resulting string (enc): yvT6h6jti9krth/BaA3cO3ABKult8N38fnEb24MS0BwnX+gc1NNgEA==
This is my little Java class trying to do the same as the perl script, but which returns a completely different value:
import java.util.Arrays;
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 Encoder {
public String encrypt(String message, String encryptionKey) throws Exception {
// handle the key
SecretKey secretKey = null;
byte[] keyValueAsBytes = Arrays.copyOf(encryptionKey.getBytes("UTF-8"), 24);
DESedeKeySpec keySpec = new DESedeKeySpec(keyValueAsBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
secretKey = keyFactory.generateSecret(keySpec);
// cipher
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// encode
byte[] plainText = message.getBytes("UTF-8");
byte[] encryptedText = cipher.doFinal(plainText);
return Base64.encodeBase64String(encryptedText);
}
public static void main(String[] args) throws Exception{
String secretKey = "bC5PEcLzvb+jY1FZWuP4pw50";
String message = "subscriptionId=0214288302000000207";
Encoder enc = new Encoder();
System.out.println(enc.encrypt(message, secretKey));
//returns:hw6JzwdvmjwORzmitXcQ6vsmskK6vtdIObu+KYiGW4D4DRwNGHEX2w==
}
}
Any suggestions and help would be much appreciated.
I have to make an app in JS which encodes a message with AES and passes it to a server via AJAX. Then the server and decodes the message using Java.
My question is: How to encrypt a message in JS and be able to decrypt it in Java using AES? Knowing that the communication between java and js is already established via webservices
Client-side, I use the Crypto JS library (http://code.google.com/p/crypto-js/). Server-side I use the Cipher class provided with Java (I use the Java Play framework but it doesn't matter here).
I'm totally new to cryptography. I've made researches all day long and still can't make this work.
The problem is that the key used to encrypt and decrypt the message must be the same, and I don't know how to do this.
From my searches, I understand there is different modes to use AES. By default Java uses ECB and CryptoJS uses CBC which is a problem, but which seems not so hard to fix, by telling CryptoJS to use the ECB mode too. But then there is a padding problem, it seems the only padding available in Java and CryptoJS is no padding at all. But when I use NoPadding in Java I get an exception.
But even if I manage to fix this, the huge problem is that the key generated by CryptoJS and the one generated by Java are not the same. If I encrypt a message in Java the result is always the same, in Hex. But in crypto JS it is in Base64 and it is never the same....
I understand this is caused by the key generation which isn't the same in Java and CryptoJS (then enters the notion of IV and Salt which are blur for me).
Don't do encryption in browser JS; it's impossible to do securely.
Use SSL. Its intended purpose is to encrypt communication between a browser and a server.
If cost is your problem, there are free SSL certificates.
I recently invested days in this problem. I tried a lot a lot of libraries on the java and javascript side. This blog saved my day: http://watchitlater.com/blog/tag/aes/ .
So it can be done! Just use Gibberish AES (https://github.com/mdp/gibberish-aes) an the javascript side and bouncycastle an on the java side.
The java side encryption might look like this(leaned on the above mentioned blog):
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.spec.InvalidKeySpecException;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Encoder;
public class OpenSSLEncryption {
private static final String CIPHER_ALG = "PBEWITHMD5AND256BITAES-CBC-OPENSSL";
private static final Provider CIPHER_PROVIDER = new BouncyCastleProvider();
private static final String PREFIX = "Salted__";
private static final String UTF_8 = "UTF-8";
private String password;
private PBEKeySpec pbeSpec;
private SecretKeyFactory keyFact;
private Cipher cipher;
private Random rand = new Random();
private BASE64Encoder encoder = new BASE64Encoder();
public OpenSSLEncryption(String password) throws NoSuchAlgorithmException, NoSuchPaddingException {
this.password = password;
pbeSpec = new PBEKeySpec(password.toCharArray());
keyFact = SecretKeyFactory.getInstance(CIPHER_ALG, CIPHER_PROVIDER);
cipher = Cipher.getInstance(CIPHER_ALG, CIPHER_PROVIDER);
}
public synchronized String encrypt(String toEncrypt) throws InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, IOException {
byte[] salt = new byte[8];
rand.nextBytes(salt);
PBEParameterSpec defParams = new PBEParameterSpec(salt, 0);
cipher.init(Cipher.ENCRYPT_MODE, keyFact.generateSecret(pbeSpec), defParams);
byte[] cipherText = cipher.doFinal(toEncrypt.getBytes(UTF_8));
ByteArrayOutputStream baos = new ByteArrayOutputStream(cipherText.length + 16);
baos.write(PREFIX.getBytes(UTF_8));
baos.write(salt);
baos.write(cipherText);
baos.close();
return encoder.encode(baos.toByteArray());
}
}