AES secret key lost on application start - java

I am using AES encryption and have encrypted a message with a key generated from key generator. Encryption and Decryption is working as expected.
But when the application is restarted and I am trying to decrypt the same message again, it is giving error as the secret key is not stored anywhere which was used to encrypt the message and on restart of application we don't have that secret key anymore.
Cannot invoke javax.crypto.Cipher.getIV() because AES.encryptionCipher is null.
I don't want to store the secret key in database. Can I store the secretKey and write it in a file and place that file in src/main/resources folder.Will it be a good approach?
Is there any other place where I could store the secret key and at the time of decrypting the meesages I could load the key from that place and decrypt messages.
This is what I have done so far.
private static SecretKey key;
private static int KEY_SIZE = 128;
private static int T_LEN = 128;
private static Cipher encryptionCipher;
public static String encrypt(String message) {
byte[] messageInBytes = message.getBytes();
byte[] encryptedBytes = null;
try {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_SIZE);
key = generator.generateKey();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
encryptedBytes = encryptionCipher.doFinal(messageInBytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return encode(encryptedBytes);
}
public static String decrypt(String encryptedMessage) {
byte[] messageInBytes = decode(encryptedMessage);
byte[] decryptedBytes = null;
try {
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(T_LEN, encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
decryptedBytes = decryptionCipher.doFinal(messageInBytes);
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return new String(decryptedBytes);
}
private static String encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
private static byte[] decode(String data) {
return Base64.getDecoder().decode(data);
}

I had similar situation, still have some open part. Suggested solution solve some, but still there are some open question, hopefully we can have common approach with expert inputs.
Keytool to generate Secret Key
keytool -genseckey -alias aeskey -keyalg AES -keysize 128 -keypass password -keystore project.keystore -storetype pkcs12
refer the Oracle documentation for details.
Java provide standard Java keystore(JKS), Secret key can be stored in jks file before starting application and this file can be read again on restart.
JKS also need password to access, in this approach, challenge is how to keep java keystore password securely
my suggestion is keystore password can be saved securely in new platform like kubernetes ( any other suggestion?)
Reference source code to retrieve Secret Key from Java Keystore:
KeyStore ks = KeyStore.getInstance("pkcs12");
String keyStorePassword = appSecurityConfig.getJksPass(); //get keystore password from external configuration, like kubernetes secret
//appSecurityConfig is configuration class
char [] password = keyStorePassword.toCharArray();
ks.load(new FileInputStream(appSecurityConfig.getKeystoreFile()), password); //keystoreFile -> JKS file name
SecretKey secretKey = (SecretKey) ks.getKey("kskey", password); //kskey -> is secreykey alias

Related

AES-Encryption result of C# code is not same as of Java AES-Encryption

I've following aes encryption code in Java which I want to write it in C#, but it is not giving same output.
Java Code
public String doEncryptString(String salt, String password,String token) throws CryptoException {
try {
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec secretKeySpec = generateKeySpec(salt,password);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] inputBytes = token.getBytes();
byte[] outputBytes = cipher.doFinal(inputBytes);
return Base64Utils.encodeToString(outputBytes);
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
| IllegalBlockSizeException ex) {
throw new CryptoException("Error encrypting password", ex);
}
}
private SecretKeySpec generateKeySpec(String salt,String password) throws CryptoException{
try {
String generatedkey=salt+password;
byte[] key = generatedkey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
return secretKeySpec;
} catch (NoSuchAlgorithmException | IOException ex) {
throw new CryptoException("Error encrypting password", ex);
}
}
This is what I've tried in C#
public static string DoEncrypt(string salt, string password, string token)
{
var tdes = new AesManaged();
tdes.Key = GenerateKey(salt, password);
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform crypt = tdes.CreateEncryptor();
byte[] plain = Encoding.UTF8.GetBytes(token);
byte[] cipher = crypt.TransformFinalBlock(plain, 0, plain.Length);
return Convert.ToBase64String(cipher);
}
private static byte[] GenerateKey(string salt, string password)
{
string generatedkey = $"{salt}{password}";
var key = Encoding.UTF8.GetBytes(generatedkey);
var sha1 = SHA1Managed.Create();
key = sha1.ComputeHash(key);
return key.Take(16).ToArray(); // use only first 128 bit
}
string/token to encrypt : ZHKRIWB310XVVWG315PI7UZZWU1V0YYL5WE9JL
Java output: eUjNH8kcgWtlEmuCFHMPwnCFWjy5Pye/gF+itrPs1g8AjtAEZQqlzW/v7kEt2haG
My C# code output: O8sKdJWH+XCOIbexZPEwN5NxWqpWRHC5b3ZsihT8cfBqpI1eVr3PEr9Eq39a5pMn
I don't know what I am doing wrong here. Any help would be appreciated. Thanks
Update
My apologies everyone. The code translated in C# in working fine. By mistake, I was passing different salt value. Thanks everyone.
What's in TRANSFORMATION from the Java code?
You need also to use the same mode and padding to get the same results, meaning ECB and PKCS7 in your case.
Java seems to offer only PKCS5 padding? But it seems to be compatible with PKCS7? I'm not a Java dev and can't provide details, but there is a discussion here: https://crypto.stackexchange.com/questions/9043/what-is-the-difference-between-pkcs5-padding-and-pkcs7-padding where they say:
Some cryptographic libraries such as the SUN provider in Java indicate
PKCS#5 where PKCS#7 should be used - "PKCS5Padding" should have been
"PKCS7Padding". This is - with high probability - a legacy from the
time that only 8 byte block ciphers such as (triple) DES symmetric
cipher were available.
And by the way: for production never use ECB mode as it's unsafe.

Encryption using java crypto library and RSA 4096 PublicKey not working properly

I am working with a third party and we have a requirement to encrypt email addresses before sending them over the network for privacy concerns. When I encrypt the email addresses using openssl on the command line everything works fine. However my Java 8 implementation generates encrypted strings which cannot be decrypted by the third party using their private key.
The private key is retrieved from the provider on-demand because they change their public keys at some interval. Here is an example of the response for a public key which is used to create the public key locally.
{"publicKey":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAh2+Bp9cU/8W2Qp6dUhH4\n7K079gL4IKc2ZnfnkKBZFEdmXUoaVsaEk/oMf/w/coOIbl36bcZVDJrp0okuPGNf\n5XRsIUbClrdPGr/pSEszS4pEZR5PvYMBRF3uile8OikiTAKQlTg+LZhdE2MCHPOv\nLZBAHH6wOj4nO/JsjbRXsNU+JyaDnc6RpFsw6zdcFiTDBSKRW0XukthnqffayWkk\nZ2HcpgJDEq8RbYV1Bb9rObvFmid/Rxj+YdhMqrDmhG5hmPSj/QXEKnuY988aPANa\nfnIRw0JnszOL0FOulVpBLGvQc6BcIaKWxSRUJFb3sM1RKMgJsFyRVLkEaMnCtwtW\ncve8Mhvs3luPM6dvggDUYwivu31Mk8sbAsB846JmpQH4SF2A7CpHo1teX7EgwwLJ\nitX6UfjIaU9Xx6BKFPcW+VgqrFYY5CCBxXD8toS5dbI14RQVUHz+3wFywtBRHO/Q\njk/u83wgdKDH38+TeBiYLUNqZ3DU5E5PU21eOtqTQ7T3g8L1bcq9zhrXGf0ONNOS\nEL0RCGvMgGm5nSMkV1maaYJpd1ArufrIDoSIiK+twpx7Rgkwxe7xPCT4LtJ+lQoC\nswDHd7kxVa4Toa2SqqT79S4+0Z52+Ke4tfRujEkPv5m6oCUwBcUhPGN7K6ie/E7X\nJN9kIq7QLV3ef+QUbzYiZZsCAwEAAQ==\n-----END PUBLIC KEY-----\n","encryptionKeyId":"ENCRYPTION-KEY-ID-2020-04-01T18:00:13.367Z"}
Here is an example of encrypting strings using the command line that does work:
First receive the latest public key from the provider.
curl -s "http://stg.api.bazaarvoice.com/notifications/rsa_encryption_key/public/current/" | jq -r '.publicKey' > public_staging_key.pem
echo "testuser3#mailtest.nexus.bazaarvoice.com" | openssl rsautl -encrypt -pubin -inkey public_staging_key.pem | xxd -p | tr -d '\n' > encrypted_example_1.txt
This encrypted email address can be properly decrypted by the third party with their private key.
Below is my java implementation which is generating email Addresses which cannot be decrypted by the third party using their private key.
#Bean
#Qualifier(value="keySettings")
public BazaarVoiceKeySettings keySettings() throws IOException {
final String uri = bazaarVoiceSettings.getPiePublicKeyUri();
RestTemplate restTemplate = new RestTemplate();
BazaarVoiceKeyResponse keyResponse = restTemplate.getForObject(uri, BazaarVoiceKeyResponse.class);
BazaarVoiceKeySettings keySettings = new BazaarVoiceKeySettings();
String pubKeyPEM = keyResponse.getPublicKey().replace("-----BEGIN PUBLIC KEY-----\n", "")
.replace("-----END PUBLIC KEY-----", "").replaceAll("\\s", "");
// create the key factory
try {
KeyFactory kFactory = KeyFactory.getInstance("RSA");
// decode base64 of your key
byte[] decodedBytes = Base64.getMimeDecoder().decode(pubKeyPEM);
// generate the public key
X509EncodedKeySpec spec = new X509EncodedKeySpec(decodedBytes);
RSAPublicKey rsaPublicKey = (RSAPublicKey) kFactory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-512ANDMGF1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
keySettings.setCipher(cipher);
keySettings.setEncryptionKeyId(keyResponse.getEncryptionKeyId());
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
return keySettings;
}
private String encryptEmail(String emailAddress) throws BadPaddingException, IllegalBlockSizeException {
Cipher cipher = keySettings.getCipher();
byte[] cipherText = cipher.doFinal(emailAddress.getBytes(Charsets.UTF_8));
String encryptedString = DatatypeConverter.printHexBinary(cipherText);
return encryptedString;
}
As #dave_thompson_085 answered, the solution was to change my encoding mode to RSA/ECB/PKCS1Padding

AES Encryption by Java code and decryption by using OpenSSL (terminal)

I am able to encrypt the data in the file using below mentioned Java code. But when I try to decrypt the encrypted file using OpenSSL from the command line then I am not be able to do that.
I tried the command
openssl enc -aes-256-cbc -d -in file_05.encrypt.txt -out file_05.decrypt.txt
It asked for a password -
enter aes-256-cbc decryption password:
I entered the password as "helloworld",
Then in the terminal it shows a "bad magic number" error message.
String pwd = "helloworld";
String SALT_VALUE = "12345#salt";
private String algorithm = "PBEWITHSHA256AND256BITAES-CBC-BC";
String finalTxt = "Hi, Goof After noon All.";
char[] pwd = Crypt.password.toCharArray();
SecretKey originalKey = Crypt.generateSK(pwd);
byte[] cipherText = Crypt.encrypt(finalTxt.getBytes(),SALT_VALUE.getBytes(), originalKey);
public static SecretKey generateSK(char[] passPhrase) throws NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidAlgorithmParameterException,
InvalidKeyException {
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase);
SecretKeyFactory secretKeyFactory;
secretKeyFactory = SecretKeyFactory.getInstance(algorithm);
return secretKeyFactory.generateSecret(pbeKeySpec);
}
public static byte[] encrypt(byte[] image, byte[] salt, SecretKey sKey) throws InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException,
InvalidKeySpecException,
UnsupportedEncodingException,
InvalidAlgorithmParameterException {
Cipher cipher;
try {
cipher = getCipher(Cipher.ENCRYPT_MODE, salt, sKey);
return cipher.doFinal(image);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static Cipher getCipher(int mode, #NonNull byte[] salt, #NonNull SecretKey secretKey) throws Exception {
PBEParameterSpec pbeParamSpecKey = new PBEParameterSpec(salt, 1000);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(mode, secretKey, pbeParamSpecKey);
return cipher;
}
It asked for a password - enter aes-256-cbc decryption password: I entered the password as "helloworld",
Then in the terminal it shows a "bad magic number" error message
Openssl uses by default its internal EVP_BytesToKey function to generate key and IV from provided password and salt. Just search on internet to find Java implementation if needed.
By default Openssl expect format Salted__<8bit_salt><ciphertext> if you don't provide key and IV directly.
I try to decrypt the encrypted file using OpenSSL from the command line then I am not be able to do that
I am not sure what your Crypt class is implemented, you may try to print hex encoded key and iv. The using openssl with parameters -iv <hex_IV> -K <hex_key> you can directly provide the IV and Key value to decrypt the ciphertext
It seems like you are missing header expected by openssl - string Salted__, followed by 8 bytes salt, followed by ciphertext.

RSA/ECB/NoPadding decryption returning null characters

I am encrypting a string using RSA algorithm and encryption and decryption logic is
public class RsaEncrypt {
private static final String ALGORITHM = "RSA";
public static void main(String[] args) {
String filePath = "/home/Desktop/abc.jks";
char[] password = "changeit".toCharArray();
String alias = "123";
KeyStore ks = null;
try {
//loading the keystore
ks = KeyStore.getInstance("JKS");
InputStream readStream = new FileInputStream(filePath);
ks.load(readStream, password);
Certificate cert = ks.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password);
RsaEncrypt e = new RsaEncrypt();
String result = e.encrypt("abvhdh", publicKey);
String decryptResult = e.decrypt(result.getBytes(), privateKey);
} catch (Exception e) {
e.printStackTrace();
}
}
//Encryption of a string
public String encrypt(String text,PublicKey publicKey) {
String retVal = null;
byte[] cipherText = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
cipherText = Base64.getEncoder().encode(cipherText);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new String(cipherText) ;
}
// Decryption of a string
private String decrypt(byte[] text, PrivateKey privatekey) {
byte[] dectyptedText = null;
try {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privatekey);
dectyptedText = cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception ex) {
ex.printStackTrace();
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new String(dectyptedText);
}
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value.
But when i decrypt,The reuslt string contains some null characters
Example
input : abcd output : abcd \u0000 \u0000 \u0000 \u0000....
How can i resolve this problem and what is the best way to get same encryption value if we encrypt multiple times?????
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
That is correct and it is even required property of the RSA encryption. By default PKCS1.5 padding is used (RSA/ECB/PKCS1Padding) which contains some random bytes. Safer option is RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING mode which is even more random.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value. But when i decrypt,The reuslt string contains some null characters
Using RSA without padding (NoPadding) is very unsafe (it is called textbook RSA).
Padding extends the original encrypted value to full space length (e.g. 2048 bits) and then the RSA magic (exponentiation) will be executed. Using the NoPadding parameter you are telling the crypto library that you will do the padding yourself. In that case you are expected to remove the padding after decryption (in your case zero padding)
I hope you are doing that for learning / academic purposes, not some real security project. You may have a look at my blog about encryption to get some examples.
btw: you should not use RSA to encrypt the plaintext itself. Rather use symmetric encryption to encrypt the plaintext and then RSA to encrypt the symmetric encryption key.

javax.crypto.BadPaddingException: Data must start with zero

First of all, this is not a duplicate question. I am facing a very strange issue.
Following is what I do.
Case 1:
Generate Key Pair
Encrypt Using Private Key
Decrypt Using Public Key
Everything works fine.
Case 2:
Load Certificate from Mozila Firefox Key Store
Use Certificate A
Encrypt Using Private Key of Certificate A
Decrypt Using Public Keu of Certificate A
Everything works fine.
Case 3:
Load Certificate from Internet Explorer Key Store
Use Certificate A
Encrypt Using Private Key of Certificate A
Decrypt Using Public Keu of Certificate A
At Decrypt time, I get error of BadPadding exception
Following is snippet of each of my codes.
Generating Key Pair
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Load Mozilla KeyStore
String strCfg = System.getProperty("user.home")+ File.separator + "jdk6-nss-mozilla.cfg";
Provider p1 = new sun.security.pkcs11.SunPKCS11(strCfg);
Security.addProvider(p1);
keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, "password".toCharArray());
Content of Config file
name=NSS
slot=2
library=C:/Program Files/Mozilla Firefox/softokn3.dll
nssArgs="configDir='C:/Documents and Settings/pratik.vohera.DIGI-CORP/Application Data/Mozilla/Firefox/Profiles/t48xsipj.default' certPrefix='' keyPrefix='' secmod='secmod.db' flags=readOnly"
Load IE KeyStore
keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
Get Public and Private key from KeyStore
if (keyStore != null) {
Enumeration<String> enumaration = null;
try {
enumaration = keyStore.aliases();
} catch (KeyStoreException e1) {
e1.printStackTrace();
}
ArrayList<String> certiList;
while (enumaration.hasMoreElements()) {
String aliases = enumaration.nextElement();
certiList = new ArrayList<String>();
certiList.add(aliases);
try {
selectedCert = keyStore.getCertificate(aliases);
selectedpublickey = (RSAPublicKey) selectedCert.getPublicKey();
selectedAlias = aliases;
selectedprivateKey = (PrivateKey) keyStore.getKey(selectedAlias, null);}
} catch (KeyStoreException e) {
e.printStackTrace();
}
}
Encryption
private static String publicEncrypt(String text, Key pubKey) throws Exception {
BASE64Encoder bASE64Encoder = new BASE64Encoder();
byte[] plainText = text.getBytes();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String encryptedText = bASE64Encoder.encode(cipher.doFinal(plainText));
return encryptedText;
}
Decryption
private static String privateDecrypt(String text, Key priKey)throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] encryptText = base64Decoder.decodeBuffer(text);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String decryptedString = new String(cipher.doFinal(encryptText));
return decryptedString;
}
Exception Stacktrace
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at test.testclass.privateDecrypt(testclass.java:198)
at test.testclass.test(testclass.java:137)
at test.testclass.main(testclass.java:120)
I have been working on this for a long time. This is very important. Do let me know if any further information is required.
In the third case, the problem is: You try to encrypt using the private key and decrypt using the public key which is wrong. You should always decrypt using the private key.

Categories

Resources