【问题标题】:Java - Storing Keypairs in KeyStoreJava - 在 KeyStore 中存储密钥对
【发布时间】:2020-05-03 22:01:48
【问题描述】:

我正在实施一个混合加密项目,并为发送方和接收方密钥(私有和公共)生成了 2 个密钥对。我在一个文件中有这些密钥。

密钥对生成代码

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;

import Decoder.BASE64Encoder;

public class GenerateRSAKeys{

    public static void main(String[] args)
    {



        String publicKeyFilename = null;
        String privateKeyFilename = null;

        publicKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_publicKey";
        privateKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_privateKey";

        GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();

        generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);

    }

    private void generate (String publicKeyFilename, String privateFilename){

        try {

            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            // Create the public and private keys
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
            BASE64Encoder b64 = new BASE64Encoder();

            SecureRandom random = createFixedRandom();
            generator.initialize(1024, random);

            KeyPair pair = generator.generateKeyPair();
            Key pubKey = pair.getPublic();
            Key privKey = pair.getPrivate();

            System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
            System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));

            BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
            out.write(b64.encode(pubKey.getEncoded()));
            out.close();

            out = new BufferedWriter(new FileWriter(privateFilename));
            out.write(b64.encode(privKey.getEncoded()));
            out.close();


        }
        catch (Exception e) {
            System.out.println(e);
        }
    }

    public static SecureRandom createFixedRandom()
    {
        return new FixedRand();
    }

    private static class FixedRand extends SecureRandom {

        MessageDigest sha;
        byte[] state;

        FixedRand() {
            try
            {
                this.sha = MessageDigest.getInstance("SHA-1");
                this.state = sha.digest();
            }
            catch (NoSuchAlgorithmException e)
            {
                throw new RuntimeException("can't find SHA-1!");
            }
        }

        public void nextBytes(byte[] bytes){

            int    off = 0;

            sha.update(state);

            while (off < bytes.length)
            {                
                state = sha.digest();

                if (bytes.length - off > state.length)
                {
                    System.arraycopy(state, 0, bytes, off, state.length);
                }
                else
                {
                    System.arraycopy(state, 0, bytes, off, bytes.length - off);
                }

                off += state.length;

                sha.update(state);
            }
        }
    }

}

现在,我需要保护这些密钥(可能不是直接在任何磁盘上)。

我在网上搜索过这个案例;将密钥保存到密钥库是保护密钥的方法,并且在读取用于加密和解密的密钥时使用相同的密钥库。

有人可以建议我如何在密钥库中保存公钥和私钥以及如何在 java 中读取它吗?

【问题讨论】:

    标签: java encryption


    【解决方案1】:

    Java KeyStore API(和底层提供者)不支持存储密钥对——即私钥和公钥(然后可以用于公钥加密和验证等操作)。它确实支持为公钥存储私钥和 证书链。证书链可以只包含一个证书,特别是如果该证书是自签名证书(根据定义,它本身就是一个链)。这正是命令行keytool -genkeypair 所做的事情:它生成一个密钥对并将私钥和公钥的自签名证书存储在(指定或默认的)密钥库文件中。

    如果您只使用“标准”JRE,则在代码中创建自签名(或其他)证书并不容易。

    但是,如果您添加了 BouncyCastle,显然您已经添加了,或者您发布的代码不起作用,那么它具有许多适用的功能。有关使用 only bcprov(旧的,已弃用的X509V3CertificateGenerator)生成证书的简单示例,请参阅我的JAVA API to create a keystore and attaching a csr and keypair to it——但按照我的建议进行操作,但没有显示:不要创建首先不需要的 CSR,而是使用所需的名称和公钥创建证书。

    更好的是,如果您也有或可以获得 bcpkix,请使用X509v3CertificateBuilder。请参阅 Self signed X509 Certificate with Bouncy Castle in JavaGenerating X509 Certificate using Bouncy Castle Java 中的示例。

    要写出和读回包含此数据的密钥库文件,请参见上面链接的 Javadoc。注意 Java 传统上默认为 JKS 格式的文件,您会在此处和网络上的其他地方找到许多较旧的答案,这些答案假设,但自 2017 年的 Java 9 以来,它默认为 PKCS12 格式,这不仅是标准的,因此(大部分)可互操作但也更安全(使用更好的 PBKDF 算法)。

    但是,您的代码使用了完全伪造的随机生成器,因此每次都生成相同的密钥对。鉴于此,没有真正需要存储它,因为您可以随时重新生成它。事实上,即使拥有这个密钥对也没有任何好处,因为它根本不提供任何安全性

    【讨论】: