使用 public 密钥在客户端和服务器之间交换对称密钥

Exchange symmetric keys between client and server using public key

我希望通过 Java RMI 在客户端和服务器程序 运行 之间交换对称密钥。

我的服务器创建了一个 public 密钥:

KeyPairGenerator keyGen = null;
try {
    keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
}
KeyPair pair = keyGen.generateKeyPair();
this.priv = pair.getPrivate();
this.pub = pair.getPublic();

我将 public 密钥发送给客户端。客户端将使用服务器的 public 密钥生成密码。我想使用这个 Cipher 来加密一个封装了客户端创建的对称密钥的 SealedObject 并将其发送到服务器。

//create cipher using server's public key
Cipher cipher = null;
try {
    cipher = Cipher.getInstance(serverKey.getAlgorithm(), "SUN");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (NoSuchPaddingException e) {
    e.printStackTrace();
} catch (NoSuchProviderException e) {
    e.printStackTrace();
}
try {
    cipher.init(Cipher.ENCRYPT_MODE, serverKey);
} catch (InvalidKeyException e) {
    e.printStackTrace();
}

但是当我运行程序初始化密码时出现异常:

java.security.NoSuchAlgorithmException: No such algorithm: DSA
at javax.crypto.Cipher.getInstance(Cipher.java:646)
at javax.crypto.Cipher.getInstance(Cipher.java:568)
at Client.main(Client.java:91)

我不明白为什么会出现此 NoSuchAlgorithm 异常。如果我用 RSA 而不是 DSA 创建 public 密钥,我不会得到这个,但 RSA 给了我:

javax.crypto.IllegalBlockSizeException: 
Data must not be longer than 117 bytes

那么我应该使用什么来安全地发送包含我的对称密钥的密封对象?

正如@zapl 评论的那样,DSA 不是加密算法,RSA 只能加密大小小于密钥大小的数据减去小的大部分固定开销(PKCS1 为 11 字节,这是默认值,因此您使用过;OAEP 多一些)。

虽然不在您发布的代码中,但我敢打赌问题出在您试图密封 SecretKey 对象。加密仅适用于字节(或位)序列,在 Java 中由字节数组或有时部分数组表示,因此 SealedObject 实际上将您提供给它的对象序列化为字节并加密这些字节,并反过来解密这些字节并反序列化它们以重新形成对象。 Java 序列化有一些开销,尤其是各种 Key 类型被组织在一个层次结构中,这加剧了这种情况。例如,从 KeyGenerator.getInstance("TDES") 序列化一个 SecretKey 是 282 个字节,对于 RSA-1024 来说太大了,正如你的例外所说,它只能是 117 个字节。

而是仅密封SecretKey.getEncoded()获得的键值的字节。在接收方(服务器)上将字节放回 SecretKeySpec 中(使用正确的算法;如果没有提前修复,也发送它)并且您实际上可以将它用作对称的 Key 即使没有 运行 它通过工厂(不同于具有更多结构的非对称密钥)。即使是简单的 byte[] 也有 一些 序列化开销,但不足以在这里引起问题。