错误的 AES 加密
Wrong AES encryption
我正在尝试做一个简单的 AES 加密工具,将明文秘密转换为 PSKC5 XML,如 RFC6030 chap. 6.1 中所示。使用其中说明的示例值,即:
- 秘密:3132333435363738393031323334353637383930
- 加密密钥:12345678901234567890123456789012
- MAC 密钥:1122334455667788990011223344556677889900
- HOTP 的 IV:000102030405060708090a0b0c0d0e0f
- MAC 的 IV:11223344556677889900112233445566
但是 AES 摘要的 IV 太长,MAC class 不接受用于签名的 IV。我试图忽略 MAC IV 并将 HOTP IV 转换为
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
但我得到的结果与示例中所示的结果不同。这是我正在使用的class:
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;
}
}
我哪里错了?
更新:这两个 IV 是我必须分别用来加密秘密和 MAC 密钥的 IV,它们是十六进制数组,但我的结果仍然与示例显示的不同。有什么线索吗?
我认为您对加密、编码等内容有点困惑。我添加了一个简单的方法 concatByteArrays
,然后我重写了您的 encryptSingleSecret
方法(见下文)。您错过的一件事是,您必须将用于加密某些内容的 IV 添加到生成的密码中,然后对整个 shebang 进行 base64 编码。另外,您对 MAC 部分的理解有点偏差。
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();
}
}
我正在尝试做一个简单的 AES 加密工具,将明文秘密转换为 PSKC5 XML,如 RFC6030 chap. 6.1 中所示。使用其中说明的示例值,即:
- 秘密:3132333435363738393031323334353637383930
- 加密密钥:12345678901234567890123456789012
- MAC 密钥:1122334455667788990011223344556677889900
- HOTP 的 IV:000102030405060708090a0b0c0d0e0f
- MAC 的 IV:11223344556677889900112233445566
但是 AES 摘要的 IV 太长,MAC class 不接受用于签名的 IV。我试图忽略 MAC IV 并将 HOTP IV 转换为
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
但我得到的结果与示例中所示的结果不同。这是我正在使用的class:
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;
}
}
我哪里错了?
更新:这两个 IV 是我必须分别用来加密秘密和 MAC 密钥的 IV,它们是十六进制数组,但我的结果仍然与示例显示的不同。有什么线索吗?
我认为您对加密、编码等内容有点困惑。我添加了一个简单的方法 concatByteArrays
,然后我重写了您的 encryptSingleSecret
方法(见下文)。您错过的一件事是,您必须将用于加密某些内容的 IV 添加到生成的密码中,然后对整个 shebang 进行 base64 编码。另外,您对 MAC 部分的理解有点偏差。
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();
}
}