0
votes

I am trying to decrypt a message that has been encrypted using AES-128 Symmetric encryption and then RSA-1024 Asymmetric encryption on the generated symmetric key. I receive the encrypted AES key and the encrypted message, extract the private key from the pfx file and then proceed with the decryption of the symmetric key. After that, I try to decrypt the encrypted message using the decrypted AES key.

Following is my code:

  // Get the private key
  PrivateKey privateKey = (PrivateKey) keyStore.getKey(selectedAlias, "password".toCharArray());
  System.out.println("Key information " + privateKey.getAlgorithm() + " " + privateKey.getFormat());

  // Load aesSessionKey and encryptedMessage
  byte[] aesSessionKey = ...
  byte[] encryptedMessage = ...

  // RSA Decryption of Encrypted Symmetric AES key - 128 bits
  Cipher rsaCipher = Cipher.getInstance("RSA", "BC");
  rsaCipher.init(Cipher.UNWRAP_MODE, privateKey);
  Key decryptedKey = rsaCipher.unwrap(aesSessionKey, "AES", Cipher.SECRET_KEY);
  System.out.println("Decrypted Key Length: " + decryptedKey.getEncoded().length);

  SecretKeySpec decrypskeySpec = new SecretKeySpec(decryptedKey.getEncoded(), "AES");
  Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING", "BC");
  cipher.init(Cipher.DECRYPT_MODE, decryptedKey, new IvParameterSpec(new byte[16]));
  byte[] message = cipher.doFinal(encryptedMessage);
  System.out.println(new String(message, "UTF-8"));

The problem is that the size of the decrypted AES key is 128 bytes and not 16 bytes as I was expecting. I get the following exception:

Key information RSA PKCS#8
Decrypted Key Length: 128
java.security.InvalidKeyException: Key length not 128/192/256 bits.
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at com.simarks.services.PKCS12.run(PKCS12.java:74)
at com.simarks.services.PKCS12.main(PKCS12.java:34)

I am new to Java Cryptography. I have checked numerous other questions and tried several different approaches (like using DECRYPT_MODE instead of UNWRAP_MODE) but I get the same error. Any help will be much appreciated.


EDIT: The client code encrypting the message is this:

PBYTE          pInputData = NULL;
DWORD          dwInputSize = 0;
PBYTE          pCertData = NULL;
DWORD          dwCertSize = 0;
PCCERT_CONTEXT pCertContext = NULL;
HCRYPTPROV     hCryptProv = NULL;
HCRYPTKEY      hPublicKey = NULL;
HCRYPTKEY      hSessionKey = NULL;
BYTE           InitializationVector[ 32 ] = { 0 };
DWORD          PKCS5Padding = PKCS5_PADDING;
DWORD          CBCMode = CRYPT_MODE_CBC;
PSIMPLEBLOB    pKeyBlob = NULL;
DWORD          dwBlobSize = 0;
DWORD          dwKeySize = 0;
PBYTE          pEncryptedData = NULL;
DWORD          dwEncryptedDataSize = 0;
HRESULT        hr = S_FALSE;

 if( FAILED( hr = ReadBinaryFile( InputFile, &pInputData, &dwInputSize ) ) ) goto EncryptExit;
 if( FAILED( hr = ReadBinaryFile( CertFile, &pCertData, &dwCertSize ) ) ) goto EncryptExit;
 if( ( pCertContext = CertCreateCertificateContext( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, pCertData, dwCertSize ) ) == NULL ) goto EncryptExit;
 if( !CryptAcquireContext( &hCryptProv, NULL, GetMsAesProviderName(), PROV_RSA_AES, 0 ) ) goto EncryptExit;
 if( !CryptImportPublicKeyInfo( hCryptProv, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, &pCertContext->pCertInfo->SubjectPublicKeyInfo, &hPublicKey ) ) goto EncryptExit;

 if( !CryptGenKey( hCryptProv, AlgId, CRYPT_EXPORTABLE, &hSessionKey ) ) goto EncryptExit;
 if( !CryptSetKeyParam( hSessionKey, KP_IV, InitializationVector, 0 ) ) goto EncryptExit;
 if( !CryptSetKeyParam( hSessionKey, KP_PADDING, (PBYTE)&PKCS5Padding, 0 ) ) goto EncryptExit;
 if( !CryptSetKeyParam( hSessionKey, KP_MODE, (PBYTE)&CBCMode, 0 ) ) goto EncryptExit;

 if( !CryptExportKey( hSessionKey, hPublicKey, SIMPLEBLOB, 0, NULL, &dwBlobSize ) ) goto EncryptExit;
 if( ( pKeyBlob = (PSIMPLEBLOB)malloc( dwBlobSize ) ) == NULL ) { hr = E_OUTOFMEMORY; goto EncryptExit; }
 if( !CryptExportKey( hSessionKey, hPublicKey, SIMPLEBLOB, 0, (PBYTE)pKeyBlob, &dwBlobSize ) ) goto EncryptExit;
 dwKeySize = dwBlobSize - sizeof( BLOBHEADER ) - sizeof( ALG_ID );

 dwEncryptedDataSize = dwInputSize;
 if( !CryptEncrypt( hSessionKey, NULL, TRUE, 0, NULL, &dwEncryptedDataSize, 0 ) ) goto EncryptExit;
 if( ( pEncryptedData = (PBYTE)malloc( dwEncryptedDataSize ) ) == NULL ) { hr = E_OUTOFMEMORY; goto EncryptExit; }
 CopyMemory( pEncryptedData, pInputData, dwInputSize );
 if( !CryptEncrypt( hSessionKey, NULL, TRUE, 0, pEncryptedData, &dwInputSize, dwEncryptedDataSize ) ) goto EncryptExit;

 if( FAILED( hr = WriteBinaryFile( OutputFile, pEncryptedData, dwInputSize ) ) ) goto EncryptExit;

 hr = WriteBinaryFile( KeyFile, pKeyBlob->Key, dwKeySize );

EncryptExit:
3
Could you try decryption using Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC") instead of using the defaults for "RSA"? For this kind of functionality you could also try the default Oracle providers by the way (so try removing the "BC" argument as well. Also print out the type of the returned key. - Maarten Bodewes
Using secure padding, preferable OAEP, not PKCS#1v1.5 is essential for the security of RSA. You really should fix the padding. - CodesInChaos

3 Answers

1
votes

AES takes a 128-bit key, which is 16 bytes long, as one byte fits eight bits.

1024-bit RSA encrypts 1024 bits (or 128 bytes) of data. You should always use padding with RSA, as this improves security and allows you to encrypt an arbitrary amount of data.

Also note that 1024-bit RSA is pretty weak against brute force attacks. 2048 bits is considered minimum.

Additionally, CBC mode with a static IV is just asking for trouble.

1
votes

Unlike many other providers, the BouncyCastle provider doesn't use padding by default when you specify the "RSA" algorithm. According to the FAQ, the BC provider will map "RSA" to "RSA/NONE/NoPadding".

As a result, your decrypted data still has the padding attached and is thus 128 bytes long. You need to determine which padding was used in the encryption of your key and ensure you specify this explicitly when creating your Cipher instance. For example:

Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC")

As owlstead mentioned in the comments, you can also consider using the standard Oracle providers for this code if you wish - it doesn't appear that BouncyCastle is necessary.

0
votes

After some research I resolved this issue by reversing the bytes of aesSessionKey:

org.apache.commons.lang.ArrayUtils.reverse(encryptedSessionKey);

Apparently CryptoAPI structures usually represent data in little-endian order but Java (and .NET) uses big-endian. Here is some of the links where I found the solution: