I'm trying to do a simple AES encryption tool that convert a plaintext secret to a PSKC5 XML as illustrated in RFC6030 chap. 6.1. Using the example values illustrated in it, namely:
But the IV for the AES digest is too long and MAC class does not accept an IV for making the signature. I tried to ignore the MAC IV and convert the HOTP IV as
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
but I'm getting different results from the ones illustrated in the example. This is the class I'm using:
import java.math.BigInteger;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class Encrypt {
private byte[] ivMac, ivHotp, sharedKey, macKey;
private SecretKey secretSharedKey, secretMacKey;
private Cipher cipher;
private AlgorithmParameterSpec cipherParamSpec;
private Mac mac;
public Encrypt(String encryptionKey, String macKey, String ivMac, String ivHotp, String csvFilePath) throws FileNotFoundException {
try {
this.ivMac = hexStr2Bytes(ivMac);
this.ivHotp = hexStr2Bytes(ivHotp);
this.sharedKey = hexStr2Bytes(encryptionKey);
this.macKey = hexStr2Bytes(macKey);
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
this.mac = Mac.getInstance("HmacSHA1");
this.mac.init(new SecretKeySpec(this.macKey, "HmacSHA1"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void encryptSingleSecret(String serialNo, String secret) {
try {
byte[] secretBytes = hexStr2Bytes(secret);
String macKeyString = Base64.encode(macKey);
cipherParamSpec = new IvParameterSpec(this.ivHotp);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(this.sharedKey, "AES"), cipherParamSpec);
byte[] secretDigested = cipher.doFinal(secretBytes);
cipherParamSpec = new IvParameterSpec(this.ivMac);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(this.sharedKey, "AES"), cipherParamSpec);
byte[] macDigested = cipher.doFinal(macKey);
String MacEncrypted = Base64.encode(macDigested);
String SecretEncrypted = Base64.encode(secretDigested);
String MacValue = Base64.encode(mac.doFinal(secretDigested));
System.out.println("MAC Key: " + MacEncrypted);
System.out.println("Secret: " + SecretEncrypted);
System.out.println("MAC ValueMac: " + MacValue);
return;
} catch (Exception ex) {
ex.printStackTrace();
return;
}
}
/**
* From RFC 6238 App. A
* @param hex
* @return
*/
public byte[] hexStr2Bytes(String hex) {
// Adding one byte to get the right conversion
// Values starting with "0" can be converted
byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
// Copy all the REAL bytes, not the "first"
byte[] ret = new byte[bArray.length - 1];
for (int i = 0; i < ret.length; i++)
ret[i] = bArray[i+1];
return ret;
}
}
What am I getting wrong?
UPDATE: The two IVs are the IVs I have to use to encrypt the secret and the MAC key respectively and they are hex arrays, but still my results are different from what the example shows. Any clue?
I think you are little confused about what gets encrypted, encoded, etc. I have added a simple method concatByteArrays
, and then I've rewritten your encryptSingleSecret
method (see below). One thing you missed is that you have to prepend the IV that was used to encrypt something to the resulting cipher, and then base64 encode the whole shebang. Also your understanding of the MAC portion was a little off.
private byte [] concatByteArrays(byte []a, byte [] b ) {
byte [] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}
public void encryptSingleSecret(String serialNo, String secret) {
try {
byte[] secretBytes = hexStr2Bytes(secret);
// First, encrypt the MAC key
cipherParamSpec = new IvParameterSpec(this.ivMac);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(this.sharedKey, "AES"),
cipherParamSpec);
byte[] encryptedMacKey = cipher.doFinal(this.macKey);
// Now, prepend the IV used to encrypt the mac key to the cipher
byte [] toBeBase64Encoded = concatByteArrays(this.ivMac, encryptedMacKey);
// Now base64-encode the result and print it out. This is for the
// <MACKey> element
System.out.println("<MACKey> <CipherValue>: " + Base64.encode(toBeBase64Encoded));
// Next, encrypt the secret
cipherParamSpec = new IvParameterSpec(this.ivHotp);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(this.sharedKey, "AES"),
cipherParamSpec);
byte[] encryptedSecret = cipher.doFinal(secretBytes);
// Now, prepend the IV used to encrypt the secret to the cipher
toBeBase64Encoded = concatByteArrays(this.ivHotp, encryptedSecret);
// Now base64-encode the result and print it out. This is for the
// <Data> element
System.out.println("<Data><Secret><CipherValue>: " + Base64.encode(toBeBase64Encoded));
// Finally, compute the MAC over the encrypted value
byte [] macValue = this.mac.doFinal(toBeBase64Encoded);
// Base64-encode the result and print it out. This is for the
// ValueMAC element
System.out.println("<Data><Secret><ValueMAC>: " + Base64.encode(macValue));
return;
} catch (Exception ex) {
ex.printStackTrace();
}
}