Java 使用 RSA 公钥加密 AES 密钥

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9658921/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 05:44:54  来源:igfitidea点击:

Encrypting AES key with RSA public key

javasecurityencryption

提问by hat

I'm writing a small application for transferring files, more or less as a way to learn more of the programmatic encryption underpinnings. The idea is to generate an RSA keypair, exchange public keys, and send the AES iv and key over for further decryption. I want to encrypt the AES key with the receivers RSA public key, like so:

我正在编写一个用于传输文件的小应用程序,或多或少是作为了解更多编程加密基础的一种方式。这个想法是生成一个 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;
}

I then write out the key value to the receiver, and decrypt it like so:

然后我将密钥值写出给接收者,并像这样解密:

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;
}

In console, on the other side, I get this as output:

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

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

Furthermore, if I create a byte array of size 16 and put the cipher.doFinal(data) output into it, the array is seemingly resized to 256 bytes (.length says so, at least). Why would this be, and further, what am I doing incorrectly?

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

edit
I solved this, and thought I'd post the issue in case someone runs into this. The problem, it turns out, was the RSA/ECB/NOPADDING. For some odd reason it was screwing up my creation of the SecretKey when I transferred it over to the client. It mighthave something to do with how I'm generating keypairs (i'm using getInstance("RSA") for that), but I'm not entirely sure.

编辑
我解决了这个问题,并认为我会发布这个问题,以防有人遇到这个问题。事实证明,问题在于 RSA/ECB/NOPADDING。出于某种奇怪的原因,当我将它转移到客户端时,它搞砸了我对 SecretKey 的创建。这可能与我生成密钥对的方式有关(我为此使用了 getInstance("RSA")),但我不确定。

采纳答案by hat

As owlstead mentioned, you cannot just use "raw" RSA without padding for encryption/decryption. For one it is very insecure, and for another, the Java libraries do not even support it. Below is the working code for the encryption/decryption of the AES key using RSA keypairs.

正如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;
}

Decryption of the AES key looks like this:

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;
}

回答by HectorLector

One thing to remember is that with RSA and actually many other algorithms including AES usually, the "useful" data that you supply isn't literally the data that's encrypted. Usually, some extra data needs to be included, for example, indicating the actual length of the data in some way, data for any integrity checking... To the user, the number of input bytes doesn't necessarily equal the number of bytes following encryption.

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

Comment Source

评论来源

To get the right key size you can use an HashAlgorithm on the key (SHA), which will give you a fixed output-size. Otherwise you can just use the first 16bytes as key and ignore the rest? Good luck.

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

回答by Maarten Bodewes

You cannot just use "raw" RSA to encrypt data without any padding. You need some kind of padding scheme, if only for security reasons. Normally "RSA/ECB/PKCS1Padding" is used. This can encrypt data up to 11 bytes less than the key size. It makes sure that the data fits in the modulus, and adds at least 8 bytes of random data to make sure that encrypting e.g. the word "Yes" twice does not result in two identical cipher texts. Finally, it makes sure you can find out the size in octets of the encrypted data, so you can simply encrypt the 16, 24 or 32 bytes that make up the AES key.

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