gpt4 book ai didi

java - CryptoAPI C++ 使用 AES 与 Java 互操作

转载 作者:行者123 更新时间:2023-11-29 06:05:15 27 4
gpt4 key购买 nike

我正在尝试使用 CryptoAPI 在 C++ 中加密并使用 SunJCE 解密 Java。我已经获得了可以工作的 RSA key ——并在测试字符串上进行了验证。但是,我的 AES key 不起作用——我得到 javax.crypto.BadPaddingException: Given final block not properly padded

C++ 加密:

// init and gen key
HCRYPTPROV provider;
CryptAcquireContext(&provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);

// Use symmetric key encryption
HCRYPTKEY sessionKey;
DWORD exportKeyLen;
CryptGenKey(provider, CALG_AES_128, CRYPT_EXPORTABLE, &sessionKey);

// Export key
BYTE exportKey[1024];
CryptExportKey(sessionKey, NULL, PLAINTEXTKEYBLOB, 0, exportKey, &exportKeyLen);

// skip PLAINTEXTKEYBLOB header
// { uint8_t bType, uint8_t version, uint16_t reserved, uint32_t aiKey, uint32_t keySize }
DWORD keySize = *((DWORD*)(exportKey + 8));
BYTE * rawKey = exportKey + 12;

// reverse bytes for java
for (unsigned i=0; i<keySize/2; i++) {
BYTE temp = rawKey[i];
rawKey[i] = rawKey[keySize-i-1];
rawKey[keySize-i-1] = temp;
}

// Encrypt message
BYTE encryptedMessage[1024];
const char * message = "Decryption Works";
BYTE messageLen = (BYTE)strlen(message);
memcpy(encryptedMessage, message, messageLen);
DWORD encryptedMessageLen = messageLen;
CryptEncrypt(sessionKey, NULL, TRUE, 0, encryptedMessage, &encryptedMessageLen, sizeof(encryptedMessage));

// reverse bytes for java
for (unsigned i=0; i<encryptedMessageLen/2; i++) {
BYTE temp = encryptedMessage[i];
encryptedMessage[i] = encryptedMessage[encryptedMessageLen - i - 1];
encryptedMessage[encryptedMessageLen - i - 1] = temp;
}

BYTE byteEncryptedMessageLen = (BYTE)encryptedMessageLen;
FILE * f = fopen("test.aes", "wb");
fwrite(rawKey, 1, keySize, f);
fwrite(&byteEncryptedMessageLen, 1, sizeof(byteEncryptedMessageLen), f);
fwrite(encryptedMessage, 1, encryptedMessageLen, f);
fclose(f);

// destroy session key
CryptDestroyKey(sessionKey);
CryptReleaseContext(provider, 0);

Java 解密:

try
{
FileInputStream in = new FileInputStream("test.aes");
DataInputStream dataIn = new DataInputStream(in);

// stream key and message
byte[] rawKey = new byte[16];
dataIn.read(rawKey);
byte encryptedMessageLen = dataIn.readByte();
byte[] encryptedMessage = new byte[encryptedMessageLen];
dataIn.read(encryptedMessage);

// use CBC/PKCS5PADDING, with 0 IV -- default for Microsoft Base Cryptographic Provider
SecretKeySpec sessionKey = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, sessionKey, new IvParameterSpec(new byte[16]));

cipher.doFinal(encryptedMessage);
}
catch (Exception e) {
e.printStackTrace();
}

在一个类似的例子中,我尝试了不反转 key 字节和不反转消息字节的排列。如果我在 java 中使用导入的 key 进行加密和解密,我会得到有效的结果。我还可以专门用 C++ 加密和解密。

问题:

  1. 我应该使用 CBC/PKCS5PADDING 吗?这是 MS_ENH_RSA_AES_PROV 的默认值吗?
  2. 归零 IV 确实是 MS_ENH_RSA_AES_PROV 的默认值吗?
  3. 是否有任何方法可以诊断 key 的具体行为方式?
  4. 我想坚持使用标准 Java 包而不是安装 BouncyCaSTLe,但是有什么不同可以使第 3 方包更好地工作吗?

最佳答案

我必须做几件事才能正确获取消息:

  1. KP_MODE明确设置为CRYPT_MODE_CBC,并将KP_IV设置为0
  2. 在Java解密中使用NoPadding
  3. 不要反转 key 或消息的字节

就诊断问题而言,最有用的一条建议是在 Java 中设置 NoPadding 以防止 BadPaddingException。这让我看到了结果——即使是错误的。

奇怪的是,RSA Java/CryptoAPI 互操作解决方案要求消息完全字节反转才能与 Java 一起工作,但 AES 不期望 key 或消息被字节反转。

CryptSetKeyParam 不允许我使用 ZERO_PADDING,但在查看解密的字节时,很明显 CryptoAPI 填充了未使用的字节数。例如, block 大小为 16,如果最后一个 block 仅使用 9 个字节,则剩余 5 个字节的值为 0x05。这是否存在潜在的安全漏洞?我是否应该用随机字节填充所有其他字节并仅使用最后一个字节来表示使用了多少填充?

工作代码(使用最后一个字节作为填充计数的 CryptoAPI 约定)如下(为简单起见,已删除对 Crypt 返回值的检查):

// init and gen key
HCRYPTPROV provider;
CryptAcquireContext(&provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);

// Use symmetric key encryption
HCRYPTKEY sessionKey;
DWORD exportKeyLen;
BYTE iv[32];
memset(iv, 0, sizeof(iv));
DWORD padding = PKCS5_PADDING;
DWORD mode = CRYPT_MODE_CBC;
CryptGenKey(provider, CALG_AES_128, CRYPT_EXPORTABLE, &sessionKey);
CryptSetKeyParam(sessionKey, KP_IV, iv, 0);
CryptSetKeyParam(sessionKey, KP_PADDING, (BYTE*)&padding, 0);
CryptSetKeyParam(sessionKey, KP_MODE, (BYTE*)&mode, 0);

// Export key
BYTE exportKey[1024];
CryptExportKey(sessionKey, NULL, PLAINTEXTKEYBLOB, 0, exportKey, &exportKeyLen);

// skip PLAINTEXTKEYBLOB header
// { uint8_t bType, uint8_t version, uint16_t reserved, uint32_t aiKey, uint32_t keySize }
DWORD keySize = *((DWORD*)(exportKey + 8));
BYTE * rawKey = exportKey + 12;

// Encrypt message
BYTE encryptedMessage[1024];
const char * message = "Decryption Works -- using multiple blocks";
BYTE messageLen = (BYTE)strlen(message);
memcpy(encryptedMessage, message, messageLen);
DWORD encryptedMessageLen = messageLen;
CryptEncrypt(sessionKey, NULL, TRUE, 0, encryptedMessage, &encryptedMessageLen, sizeof(encryptedMessage));

BYTE byteEncryptedMessageLen = (BYTE)encryptedMessageLen;
FILE * f = fopen("test.aes", "wb");
fwrite(rawKey, 1, keySize, f);
fwrite(&byteEncryptedMessageLen, 1, sizeof(byteEncryptedMessageLen), f);
fwrite(encryptedMessage, 1, encryptedMessageLen, f);
fclose(f);

// destroy session key
CryptDestroyKey(sessionKey);
CryptReleaseContext(provider, 0);

Java 解密:

try
{
FileInputStream in = new FileInputStream("test.aes");
DataInputStream dataIn = new DataInputStream(in);

// stream key and message
byte[] rawKey = new byte[16];
dataIn.read(rawKey);
byte encryptedMessageLen = dataIn.readByte();
byte[] encryptedMessage = new byte[encryptedMessageLen];
dataIn.read(encryptedMessage);

// use CBC/NoPadding, with 0 IV -- (each message is creating it's own session key, so zero IV is ok)
SecretKeySpec sessionKey = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, sessionKey, new IvParameterSpec(new byte[16]));

byte[] decryptedBlocks = cipher.doFinal(encryptedMessage);

// check versus expected message
byte[] expectedBytes = "Decryption Works -- using multiple blocks".getBytes();
Assert.assertTrue("Incorrect Message" + new String(message), Arrays.equals(message, expectedBytes));
}
catch (Exception e) {
e.printStackTrace();
}

关于java - CryptoAPI C++ 使用 AES 与 Java 互操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8763597/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com