代码之家  ›  专栏  ›  技术社区  ›  Guido

Java非对称加密:存储公钥/私钥的首选方法

  •  12
  • Guido  · 技术社区  · 15 年前

    此代码生成一对公钥/私钥:

    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
    keyGen.initialize(1024);
    KeyPair keypair = keyGen.genKeyPair();
    PrivateKey privateKey = keypair.getPrivate();
    PublicKey publicKey = keypair.getPublic();
    

    我想知道的是,您通常如何存储公钥:

    选项1:存储字节

    byte[] privateKeyBytes = privateKey.getEncoded();
    byte[] publicKeyBytes = publicKey.getEncoded();
    // ... write to file
    
    // convert bytes back to public/private keys
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
    PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
    
    EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
    PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
    

    我不喜欢将代码与具体的实现(如 PKCS8EncodedKeySpec X509EncodedKeySpec .

    选项2:存储模量和指数

    KeyFactory fact = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec pub = fact.getKeySpec(publicKey, RSAPublicKeySpec.class);
    RSAPrivateKeySpec priv = fact.getKeySpec(privateKey,RSAPrivateKeySpec.class);
    
    // store modulus and exponent as BigIntegers
    BigInteger modulus = pub.getModulus());
    BigInteger exponent = pub.getPublicExponent());
    // ... write to file
    
    // recreate public key (the same applies to the private key)
    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey pubKey = fact.generatePublic(keySpec);
    

    第二个选项更容易实现,但我不知道它是否会降低性能。

    有什么建议吗?

    3 回复  |  直到 15 年前
        1
  •  13
  •   Brian M. Carr    15 年前

    在我们的应用程序中,我们用DER格式存储公钥和私钥,这样它们就可以更容易地在Java之外使用和操作。在我们的例子中,私钥上没有密码。

    将私钥转换成更容易在Java中使用的东西:

    openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER
    

    然后您可以通过以下方式直接获得RSA私钥:

    public static RSAPrivateKey getPrivateKey(File privateKeyFile) throws IOException, GeneralSecurityException {
        byte[] keyBytes = new byte[(int)privateKeyFile.length()];
        FileInputStream fis = new FileInputStream(privateKeyFile);
        fis.read(keyBytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(spec);
        return privKey;
    }
    

    公钥类似:

    openssl rsa -in private.pem -pubout -outform DER -out public.der
    

    并且阅读它:

    public static RSAPublicKey getPublicKey(File publicKeyFile) throws IOException, GeneralSecurityException {
        byte[] keyBytes = new byte[(int)publicKeyFile.length()];
        FileInputStream fis = new FileInputStream(publicKeyFile);
        fis.read(keyBytes);
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        RSAPublicKey pubKey = (RSAPublicKey)factory.generatePublic(publicKeySpec);
        return pubKey;
    }
    

    许多人储存钥匙。出于我们的目的,我们需要在多个不同语言的应用程序之间共享相同的密钥,并且不想复制磁盘上的文件。

    无论是哪种情况,性能都不应该是一个很大的问题,因为您很可能将这些密钥存储在某种类型的单实例或缓存中,而不是每次都重新生成它们。

        2
  •  2
  •   President James K. Polk    15 年前

    实际上,无论您是否意识到这两种情况,您都在存储字节。我想在@brian m.carr answer中暗示了正确的答案,它以最自然的形式存储更高级的对象。在公钥的情况下,明显的选择是作为pkcs 1 rsapublickey asn.1结构、der编码或作为x509 subjectpublickeyinfo asn.1结构、der编码。后者是Sun提供程序提供给您的,Sun类X509EncodedKeyspec支持它。同样,pkcs8encodedKeyspec支持私钥格式。这两种格式都是标准格式,例如由OpenSSL支持。Sun倾向于(倾向于)支持现有的标准,而不是定义它们自己的标准。

        3
  •  1
  •   Alexander Kjäll    15 年前

    如果您想定义一种存储密钥的格式,那么我会选择一种可消耗的格式,这样当您想更改加密时(例如,当旧的加密变得脆弱时),它就不会中断。

    因此,我将存储编码为base64的字节,以及描述格式的字符串,“rsa”可能。

    推荐文章