I am trying AES key wrapping using another AES key and unwrapping to get the original key (RFC 3394 and RFC 5649). The way of wrapping and unwrapping the key is as follows:
Key wrapping
- Convert into byte[]
- Wrap using AES
- Get Cipher instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - Generate IvParameterSpec
- Create a SecretKey object from the wrapper key
SecretKey secretKey = new SecretKeySpec(wrapperKey, "AES"); - Initialize the cipher for encryption
cipher.init(Cipher.WRAP_MODE, secretKey, iv); - Perform wrapping
byte[] wrappedKey = cipher.wrap(new SecretKeySpec(keyToWrap, "AES")); - return wrapped key
Key unwrapping
- Convert wrapped key to byte[]
- Unwrap using AES
- Get Cipher instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - Extract IV (16 bytes)
- Create a SecretKey object from the wrapper key
SecretKey secretKey = new SecretKeySpec(wrapperKey, "AES"); - Initialize the cipher for encryption
cipher.init(Cipher.UNWRAP_MODE, secretKey , iv); - Perform unwrapping
Key unwrappedKey = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
Problem
The issue I am facing while unwrapping is that I am not getting the original key back and the difference is caused by first 16 bytes in byte array which is the size of IV. I debugged but couldn't find the reason causing this.
Dependencies used
SunJCE: Cipher.AES -> com.sun.crypto.provider.AESCipher$General aliases: [Rijndael]
Below is the output.
1.
Original Key: 00112233445566778899aabbccddeeff
Original Key in byte[] : [-45, 77, 117, -37, 109, -9, -29, -114, 121, -21, -82, -5, -13, -49, 125, 105, -90, -37, 113, -57, 93, 121, -25, -33]
Original IV : [-76, 50, 106, -33, -77, -80, -90, 53, -11, -91, 17, 25, -21, -63, -57, 17]
2.
Wrapped Key : gxLT9+PTn8rikNLJnK1RcpNqFIL/fG7EPLVsIr5mwOA=
wrapped key in byte[] : [113, 0, 125, 123, 85, 97, 65, 123, 118, 91, -22, -117, 62, 37, 117, -11, -119, 98, -128, 16, -85, -51, -47, 109, -15, -65, 123, -89, -92, -81, -78, 38]
IV from wrapped key : [113, 0, 125, 123, 85, 97, 65, 123, 118, 91, -22, -117, 62, 37, 117, -11]
3.
Unwrapped Key: RhzNRM6VgeHUTAkIpYfHnqbbccddeeff
Unwrapped Key byte[]: [22, 127, 98, 127, -117, 38, 4, -64, -6, 21, 85, 105, 38, 43, -49, -115, -90, -37, 113, -57, 93, 121, -25, -33]
=============================================
Diff seems to be due to 16 bytes (the size of IV)
org key : [-45, 77, 117, -37, 109, -9, -29, -114, 121, -21, -82, -5, -13, -49, 125, 105, -90, -37, 113, -57, 93, 121, -25, -33]
output key : [22, 127, 98, 127, -117, 38, 4, -64, -6, 21, 85, 105, 38, 43, -49, -115, -90, -37, 113, -57, 93, 121, -25, -33]
Adding code :
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class KeyWrapping {
public static void main(String[] args) throws Exception {
String originalKeyHex = "00112233445566778899aabbccddeeff";
String wrappingKeyHex = "000102030405060708090a0b0c0d0e0f";
// converting hex strings to byte arrays
byte[] originalKey = hexStringToByteArray(originalKeyHex);
byte[] wrapperKey = hexStringToByteArray(wrappingKeyHex);
// Encrypt the key
byte[] wrappedKey = aesKeyWrapEncrypt(originalKey, wrapperKey);
// Decrypt the key
byte[] unwrappedKey = aesKeyWrapDecrypt(wrappedKey, wrapperKey);
// Display results
System.out.println("Original Key: " + byteArrayToHexString(originalKey));
System.out.println("Wrapped Key : " + byteArrayToHexString(wrappedKey));
System.out.println("Unwrapped Key: " + byteArrayToHexString(unwrappedKey));
}
// AES Key Wrap Encryption
private static byte[] aesKeyWrapEncrypt(byte[] keyToWrap, byte[] wrapperKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Generate a random Initialization Vector (IV)
SecureRandom random = new SecureRandom();
byte[] ivBytes = new byte[16];
random.nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
// Create a SecretKey object from the wrapper key
SecretKey secretKey = new SecretKeySpec(wrapperKey, "AES");
// Initialize the cipher for encryption
cipher.init(Cipher.WRAP_MODE, secretKey, iv);
// Encrypt the key
byte[] wrappedKey = cipher.wrap(new SecretKeySpec(keyToWrap, "AES"));
return wrappedKey;
}
// AES Key Wrap Decryption
private static byte[] aesKeyWrapDecrypt(byte[] wrappedKey, byte[] wrappingKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Extract the Initialization Vector (IV) from the wrapped key
byte[] ivBytes = new byte[16];
System.arraycopy(wrappedKey, 0, ivBytes, 0, ivBytes.length);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
// Create a SecretKey object from the wrapping key
SecretKey secretKey = new SecretKeySpec(wrappingKey, "AES");
// Initialize the cipher for decryption
cipher.init(Cipher.UNWRAP_MODE, secretKey, iv);
// Decrypt the key
Key unwrappedKey = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
return unwrappedKey.getEncoded();
}
// Helper method to convert byte array to hex string
private static String byteArrayToHexString(byte[] array) {
return Base64.getEncoder().encodeToString(array);
}
// Helper method to convert hex string to byte array
private static byte[] hexStringToByteArray(String hexString) {
return Base64.getDecoder().decode(hexString);
}
}
Update :
Thanks @President James K. Polk for pointing out the exact issue. It worked with below fix.
// AES Key Wrap Encryption
private static byte[] aesKeyWrapEncrypt(byte[] keyToWrap, byte[] wrapperKey) throws Exception {
...
byte[] wrappedKey = cipher.wrap(new SecretKeySpec(keyToWrap, "AES"));
// prepending the IV to the wrapped key
byte[] result = new byte[ivBytes.length + wrappedKey.length];
System.arraycopy(ivBytes, 0, result, 0, ivBytes.length);
System.arraycopy(wrappedKey, 0, result, ivBytes.length, wrappedKey.length);
...