基于Java的椭圆曲线密钥封装

基于Java的椭圆曲线密钥封装,java,cryptography,rsa,public-key-encryption,elliptic-curve,Java,Cryptography,Rsa,Public Key Encryption,Elliptic Curve,从我的研究中,我了解到这是使用RSA包装AES会话密钥的正确方法: import java.security.*; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; public class Main { public static void main(String args[]) throws Exc

从我的研究中,我了解到这是使用RSA包装AES会话密钥的正确方法:

import java.security.*;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class Main {
    public static void main(String args[]) throws Exception {
        KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES");
        aesKeyGen.init(256);
        SecretKey secretKey = aesKeyGen.generateKey();

        Base64.Encoder encoder = Base64.getEncoder().withoutPadding();
        Cipher cipher = Cipher.getInstance("RSA");
        KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA");
        rsaKeyGen.initialize(4096);
        KeyPair keyPair = rsaKeyGen.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        cipher.init(Cipher.WRAP_MODE, publicKey);
        byte[] wrappedKey = cipher.wrap(secretKey);
        cipher.init(Cipher.UNWRAP_MODE, privateKey);
        SecretKey unwrappedKey = (SecretKey)cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);

        System.out.println(encoder.encodeToString(wrappedKey));
        System.out.println(encoder.encodeToString(secretKey.getEncoded()));
        System.out.println(encoder.encodeToString(unwrappedKey.getEncoded()));
    }
}
我的问题是:

  • 我应该继续使用
    Cipher.getInstance(“RSA”)
    ,切换到
    Cipher.getInstance(“RSA/ECB/PKCS1PDADING”)
    还是其他什么
  • 除了使用EC,我如何做与上面相同的事情
  • 密钥封装与密钥封装相同吗
  • 我是否需要以某种方式清除
    secretKey
    使用的内存,就像我们对数组所做的那样(例如
    arrays.fill(array,(byte)0)
    ),或者只要调用
    getEncoded()
为了回答我的第二个问题,我发现我应该使用:

KeyPairGenerator ecKeyGen=KeyPairGenerator.getInstance(“EC”)

但是,我不确定在这里使用什么来代替

Cipher-Cipher=Cipher.getInstance(“”)

我还发现,通过这样做,我可以选择要使用的曲线:

...
ECGenParameterSpec ecsp = new ECGenParameterSpec("sect571k1"); 
ecKeyGen.initialize(571);
...
但是,我也不知道我应该在这里使用什么参数来代替

新的ECGenParameterSpec(“”)

我相信一些好的候选人如下:

secp521r1、nistp521、sect571r1、nistb571、nistb571、sect571k1、nistk571、nistk571、sect163r2

还有其他选择吗?你能帮我挑一个最好的吗?实例化一个
ECGenParameterSpec
对象来设置EC
KeyPairGenerator
的初始化参数是否足够

我应该继续使用Cipher.getInstance(“RSA”),切换到Cipher.getInstance(“RSA/ECB/PKCS1Padding”)还是其他什么

至少应切换到完全指定的字符串;不同的提供者可能有不同的默认值,指定完整的方案更容易阅读/维护;不是每个人都知道默认值

然而,PKCS#1 v1.5填充容易受到某些填充攻击,因此切换到OAEP填充将提供某些安全好处

除了使用EC,我如何做与上面相同的事情

不容易。RSA问题允许签名生成和加密,而(EC)DH问题允许签名生成和Diffie Hellman。然而,可以使用ECDH实现ECIES,ECIES“派生”一个对称密钥,然后用于加密消息

ECIES不包括在普通Java运行时中,但Bouncy Castle有实现,当然可以在ECDH之上构建功能,ECDH包含在
KeyAgreement
类中

密钥封装与密钥封装相同吗

它们是相关的。密钥封装更多地用于会话密钥,通常派生会话密钥,而不是对其进行加密。即,在这种情况下,您不能使用密钥封装(直接)加密现有密钥。密钥包装确实加密现有密钥,并且可能使用专门的(确定性)加密方案——尽管Java运行时提供的方案并非如此

Java中的包装非常有用,因为您不需要将被包装的密钥的中间键值存储在字节数组中;无论如何,如果密钥是在硬件(如智能卡)中管理的,这是不可能的

我是否需要以某种方式清除secretKey使用的内存,就像我们对数组(例如arrays.fill(array,(byte)0))或调用getEncoded()一样

由于字段无法直接访问,因此无法访问。Java8和更高版本实现,这样您就有了销毁密钥的方法。如果密钥在硬件中,则此方法可能会引发异常

我相信一些好的候选人如下:

secp521r1、nistp521、sect571r1、nistb571、nistb571、sect571k1、nistk571、nistk571、sect163r2

还有其他选择吗

当然,对于较低位数:例如secp384r1和secp256r1

你能帮我挑一个最好的吗

定义最佳。性能vs安全性,但即使是secp256r1也已经相当强大了。我会选择秒素数(p)曲线,而不是科布伦茨曲线或扭曲曲线(k或t曲线)

实例化一个
ECGenParameterSpec
对象来设置EC
KeyPairGenerator
的初始化参数是否足够


是。

ECIES不包括在正常的Java运行时中:实际上,它是JCE的
Cipher.getInstance()例程支持/必需的算法名称。我仍然不知道如何使用它将现有的对称会话密钥与EC创建的公钥包装在一起,但至少名称是存在的。@TiStrga它在列表中,但定义不正确,并且它不包括在提供JDK附带的椭圆曲线加密的提供程序中。你可能需要包括一些像Bouncy Castle这样的东西。但即便如此,由于算法定义不清,最好还是自己使用
keyargreement
然后使用您选择的密码。没错,它的指定非常少,而且我从未让ECIES
cipher
执行任何操作,只抛出异常。我不敢尝试使用
KeyAgreement
做任何事情,因为我不知道在我们自己的场景中第二个密钥来自哪里。(我希望我能找到一些文档和示例;很可能这与最初提问者的情况太不一样。)@TiStrga后面的部分能帮到你吗?当然可以,非常感谢!似乎没有办法让EC
KeyAgreement
与其他类型的密钥包装、现有会话密钥和多方密钥一起工作——至少在密钥处理方面具有危险的“创造性”。我们暂时搁置这个项目。