我正在编写一个用于传输文件的小应用程序,或多或少是一种了解更多程序加密基础的方法。我们的想法是生成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: <hex string>
Key decrypted, length is 256
java.security.InvalidKeyException: Invalid AES key length: 256 bytes
此外,如果我创建一个大小为16的字节数组并将cipher.doFinal(data)输出放入其中,那么该数组似乎被调整为256字节(.length表示至少如此)。为什么会这样,而且,我做错了什么?
修改
我解决了这个问题,并且认为我会发布这个问题以防万一有人碰到这个问题。事实证明,问题是RSA / ECB / NOPADDING。出于某种奇怪的原因,当我将它转移到客户端时,它正在搞砸我创建的SecretKey。 可能与我如何生成密钥对有关(我正在使用getInstance(“RSA”)),但我不完全确定。
答案 0 :(得分:5)
正如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;
}
答案 1 :(得分:4)
您不能只使用“原始”RSA来加密数据而无需任何填充。如果出于安全原因,您需要某种填充方案。通常使用“RSA / ECB / PKCS1Padding”。这可以加密比密钥大小少11个字节的数据。它确保数据符合模数,并添加至少8个字节的随机数据,以确保加密例如单词“是”两次不会产生两个相同的密文。最后,它确保您可以找到加密数据的八位字节大小,因此您可以简单地加密构成AES密钥的16,24或32个字节。
答案 2 :(得分:0)
要记住的一件事是RSA和其他很多 通常包括AES的算法,您提供的“有用”数据 字面上并不是加密的数据。通常,一些额外的数据 需要包括,例如,表明的实际长度 数据以某种方式,数据进行任何完整性检查......对于用户而言 输入字节数不一定等于字节数 加密后。
要获得正确的密钥大小,您可以在密钥(SHA)上使用HashAlgorithm,这将为您提供固定的输出大小。否则你可以使用前16字节作为键而忽略其余部分?祝你好运。