我想使用CryptoAPI在C++中進行加密,並使用SunJCE對Java進行解密。我已經獲得了RSA密鑰的工作 - 並在一個測試字符串上進行了驗證。但是,我的AES密鑰無法使用 - 我收到javax.crypto.BadPaddingException: Given final block not properly padded
。CryptoAPI C++使用AES與Java互操作
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);
爪哇解密:
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();
}
在類似的例子中,我試圖不可逆密鑰的字節,並在該消息中沒有反轉的字節排列。如果我使用java導入的密鑰進行加密和解密,我會得到有效的結果。我也可以用C++專門加密和解密。
問題:
- 我應該使用CBC/PKCS5PADDING?這是
MS_ENH_RSA_AES_PROV
的默認值嗎? - 是否歸零IV確實是
MS_ENH_RSA_AES_PROV
的默認值? - 有沒有什麼方法可以診斷密鑰行爲的細節?
- 我想堅持使用標準的Java包,而不是安裝BouncyCastle的,但在那裏,這將使第三方包工作做得更好什麼不同嗎?
如果使用加密,它可能是高度完整的軟件(或至少某些部分)。停止忽略來自CryptoAPI的返回值。 – jww 2012-01-06 22:34:26
缺少的返回值只是爲了簡化這裏的代碼。實際的單元測試檢查所有返回代碼。 – RunHolt 2012-01-09 11:26:17