使用RSA公钥加密AES密钥

我正在编写一个用于传输文件的小应用程序,或多或少是一种学习更多程序化加密基础的方法。 我们的想法是生成RSA密钥对,交换公钥,并发送AES iv和密钥以进行进一步解密。 我想用接收器RSA公钥加密AES密钥,如下所示:

// encode the SecretKeySpec private byte[] EncryptSecretKey () { Cipher cipher = null; byte[] key = null; try { cipher = Cipher.getInstance("RSA/ECB/NOPADDING"); // contact.getPublicKey returns a public key of type Key cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey() ); // skey is the SecretKey used to encrypt the AES data key = cipher.doFinal(skey.getEncoded()); } catch(Exception e ) { System.out.println ( "exception encoding key: " + e.getMessage() ); e.printStackTrace(); } return key; } 

然后我将键值写入接收器,并像这样解密:

 private SecretKey decryptAESKey(byte[] data ) { SecretKey key = null; PrivateKey privKey = null; Cipher cipher = null; System.out.println ( "Data as hex: " + utility.asHex(data) ); System.out.println ( "data length: " + data.length ); try { // assume this loads our private key privKey = (PrivateKey)utility.loadLocalKey("private.key", false); cipher = Cipher.getInstance("RSA/ECB/NOPADDING"); cipher.init(Cipher.DECRYPT_MODE, privKey ); key = new SecretKeySpec(cipher.doFinal(data), "AES"); System.out.println ( "Key decrypted, length is " + key.getEncoded().length ); System.out.println ( "data: " + utility.asHex(key.getEncoded())); } catch(Exception e) { System.out.println ( "exception decrypting the aes key: " + e.getMessage() ); e.printStackTrace(); return null; } return key; } 

在控制台中,另一方面,我将其作为输出:

 read_bytes for key: 16 data length: 16 Data as hex:  Key decrypted, length is 256 java.security.InvalidKeyException: Invalid AES key length: 256 bytes 

此外,如果我创建一个大小为16的字节数组并将cipher.doFinal(data)输出放入其中,那么该数组似乎被调整为256字节(.length就是这样说的,至少)。 为什么会这样,而且,我做错了什么?

编辑
我解决了这个问题,并且认为我会发布这个问题以防万一有人碰到这个问题。 事实certificate,问题在于RSA / ECB / NOPADDING。 出于某些奇怪的原因,当我将它转移到客户端时,它正在搞砸我创建的SecretKey。 它可能与我如何生成密钥对有关(我正在使用getInstance(“RSA”)),但我不完全确定。

您不能只使用“原始”RSA来加密数据而无需任何填充。 如果出于安全原因,您需要某种填充方案。 通常使用“RSA / ECB / PKCS1Padding”。 这可以加密比密钥大小少11个字节的数据。 它确保数据符合模数,并添加至少8个字节的随机数据,以确保加密例如单词“是”两次不会产生两个相同的密文。 最后,它确保您可以找到加密数据的八位字节大小,因此您可以简单地加密构成AES密钥的16,24或32个字节。

正如owlstead所提到的,你不能只使用“原始”RSA而不用填充加密/解密。 一方面它非常不安全,另一方面,Java库甚至不支持它。 以下是使用RSA密钥对加密/解密AES密钥的工作代码。

 private byte[] EncryptSecretKey () { Cipher cipher = null; byte[] key = null; try { // initialize the cipher with the user's public key cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey() ); key = cipher.doFinal(skey.getEncoded()); } catch(Exception e ) { System.out.println ( "exception encoding key: " + e.getMessage() ); e.printStackTrace(); } return key; } 

AES密钥的解密如下所示:

 private SecretKey decryptAESKey(byte[] data ) { SecretKey key = null; PrivateKey privKey = null; Cipher cipher = null; try { // this is OUR private key privKey = (PrivateKey)utility.loadLocalKey( ConfigFrame.privateKeyLocation, false); // initialize the cipher... cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privKey ); // generate the aes key! key = new SecretKeySpec ( cipher.doFinal(data), "AES" ); } catch(Exception e) { System.out.println ( "exception decrypting the aes key: " + e.getMessage() ); return null; } return key; } 

需要记住的一点是,通过RSA和实际上包括AES在内的许多其他算法,您提供的“有用”数据并不是字面上加密的数据。 通常,需要包含一些额外的数据,例如,以某种方式指示数据的实际长度,用于任何完整性检查的数据……对于用户来说,输入字节的数量不一定等于字节数加密后。

评论来源

要获得正确的密钥大小,您可以在密钥(SHA)上使用HashAlgorithm,这将为您提供固定的输出大小。 否则你只能使用前16字节作为键而忽略其余部分? 祝你好运。