【问题标题】:Encrypting AES key with RSA public key使用 RSA 公钥加密 AES 密钥
【发布时间】:2018-03-09 21:44:04
【问题描述】:

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

【问题讨论】:

  • 您好,请将您的完整答案作为答案发布,而不是在您的问题中进行编辑(您告诉我们问题出在哪里,而不是答案)。然后你可以在一段时间后接受你自己的答案。或者接受我的,这就解释了为什么你应该使用"RSA/ECB/PKCS1Padding" 而不是"RSA/ECB/NoPadding"...
  • 请注意,当您创建执行加密的在线协议时,您应该使用完整性保护(例如签名)。甚至 RSA 加密也可能适用于例如填充预言机攻击。没有完整性保护是一个常见错误,尽管如果您使用 PKCS#1 v1.5(由 "RSA/ECB/PKCS1Padding" 暗示),此问题会受到限制。
  • 作为旁注,ECB 很少是块密码的好模式。
  • 据我了解,核心 javax.crypto 库不支持任何其他形式的分组密码,因此我有点坚持。
  • 至少可以从 ECB 构建 CTR。你只需要小心创建一个好的随机数。但如果图书馆至少不支持 CBC 模式,我会感到惊讶。

标签: java security encryption


【解决方案1】:

正如 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/ECB/PKCS1Padding”而不是使用提供程序默认值(在当前 Oracle JCE 提供程序中返回相同的结果,但您是直接指定它更安全)。
【解决方案2】:

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

【讨论】:

  • 当然,您也可以使用继任者 OAEP,但这并不是真正需要的,并且可能不如默认的 PKCS#1 v1.5 填充方案兼容。
【解决方案3】:

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

Comment Source

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

【讨论】:

  • 你说得对,它看起来确实在填充到密钥长度(在我的情况下为 2048 位密钥)。我也看不到使用 Java 7 的 Cipher 对象支持 NOPADDING,但它并没有向我抛出异常。我想我会调查解析出我需要的真实数据。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-23
  • 2011-11-07
  • 2013-05-07
  • 1970-01-01
  • 2011-11-10
相关资源
最近更新 更多