【问题标题】:How to use keystore in Java to store private key?如何在 Java 中使用 keystore 来存储私钥?
【发布时间】:2012-04-11 00:43:24
【问题描述】:

我使用KeyPairGenerator 生成了一个 RSA 密钥对。如果我没记错的话,KeyStore 仅用于存储证书而不是密钥。如何在计算机上正确存储私钥?

【问题讨论】:

    标签: java keystore


    【解决方案1】:

    注意:此代码仅用于演示目的。将私钥存储在磁盘上时必须对其进行加密。不要按原样使用它。

    你可以这样做:

     KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
     kpg.initialize(2048);
    
     KeyPair kp = kpg.genKeyPair();
    
     KeyFactory fact = KeyFactory.getInstance("RSA");
    
     RSAPublicKeySpec pub = fact.getKeySpec(kp.getPublic(),
            RSAPublicKeySpec.class);
     saveToFile(PUBLIC_KEY_FILE, 
            pub.getModulus(), pub.getPublicExponent());
    
     RSAPrivateKeySpec priv = fact.getKeySpec(kp.getPrivate(),
            RSAPrivateKeySpec.class);
     saveToFile(PRIVATE_KEY_FILE, 
             priv.getModulus(), priv.getPrivateExponent());
    

    保存功能:

    private static void saveToFile(String fileName,
                                   BigInteger mod, BigInteger exp) 
        throws SomeException {
        ObjectOutputStream oout = new ObjectOutputStream(
                new BufferedOutputStream(new FileOutputStream(fileName)));
        try {
            oout.writeObject(mod);
            oout.writeObject(exp);
        } catch (Exception e) {
            throw new SomeException(e);
        } finally {
            oout.close();
        }
    }
    

    然后以同样的方式阅读:

    private static PublicKey readPublicKey() throws SomeException {
        InputStream in = new FileInputStream(PUBLIC_KEY_FILE);
        ObjectInputStream oin =
                new ObjectInputStream(new BufferedInputStream(in));
        try {
            BigInteger m = (BigInteger) oin.readObject();
            BigInteger e = (BigInteger) oin.readObject();
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
            KeyFactory fact = KeyFactory.getInstance("RSA");
            PublicKey pubKey = fact.generatePublic(keySpec);
            return pubKey;
        } catch (Exception e) {
            throw new SomeException(e);
        } finally {
            oin.close();
        }
    }
    

    读取私钥类似。

    【讨论】:

    • @segfault,另外请记住,您需要 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 才能将其添加到 AndroidManifest.xml
    • 将私钥存储在可通过其他应用程序访问的存储中是否安全? (如果我错了,请纠正我,但这就是这里所做的。)
    • 您可以在操作系统级别设置文件的权限(例如,应用程序的专用用户等)。显然,它超出了这个答案的范围。
    • 所以你不使用keystore这意味着你没有回答问题
    • 这是我见过的最危险的安全/代码黑客之一。您甚至不建议保护私钥。
    【解决方案2】:

    此代码块将生成 KeyPair 并将其存储在 AndroidKeyStore 上。 (注意:省略异常捕获)

    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
    keyStore.load(null);
    
    String alias = "my_key"; // replace as required or get it as a function argument
    
    int nBefore = keyStore.size(); // debugging variable to help convince yourself this works
    
    // Create the keys if necessary
    if (!keyStore.containsAlias(alias)) {
    
        Calendar notBefore = Calendar.getInstance();
        Calendar notAfter = Calendar.getInstance();
        notAfter.add(Calendar.YEAR, 1);
        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
                        .setAlias(alias)
                        .setKeyType("RSA")
                        .setKeySize(2048)
                        .setSubject(new X500Principal("CN=test"))
                        .setSerialNumber(BigInteger.ONE)
                        .setStartDate(notBefore.getTime())
                        .setEndDate(notAfter.getTime())
                        .build();
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
        generator.initialize(spec);
    
        KeyPair keyPair = generator.generateKeyPair();
    }
    int nAfter = keyStore.size();
    Log.v(TAG, "Before = " + nBefore + " After = " + nAfter);
    
    // Retrieve the keys
    KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
    RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();
    RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
    
    Log.v(TAG, "private key = " + privateKey.toString());
    Log.v(TAG, "public key = " + publicKey.toString());
    

    【讨论】:

    【解决方案3】:

    http://snipplr.com/view/18368/

    http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html

    http://java.sun.com/docs/books/tutorial/security/apisign/vstep2.html 这是最有前途的

    在不受信任的环境中保护密钥是不可能的。你可以混淆你的代码,你可以从任意变量创建一个密钥,无论如何。最终,假设您使用标准 javax.crypto 库,您必须调用 Mac.getInstance(),稍后您将在该实例上调用 init()。想要你的钥匙的人会得到它。

    但是,我认为解决方案是将密钥与环境而非程序相关联。签名意味着数据来自已知来源,并且自该来源提供以来未被篡改。目前,您正试图说“保证我的程序产生了数据”。相反,将您的要求更改为“保证我的程序的特定用户生成数据”。然后将责任转移给该用户来照顾他/她的密钥。

    【讨论】:

      【解决方案4】:

      根据您的私钥格式,您可能需要将其转换为 java keytool 可以使用的格式。

      但如果它是 keytool 支持的格式,您应该可以使用 keytool 导入它。 更多信息:

      http://docs.oracle.com/javase/tutorial/security/toolfilex/rstep1.html

      http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/keytool.html

      【讨论】:

        猜你喜欢
        • 2012-06-26
        • 1970-01-01
        • 2018-04-16
        • 2013-02-05
        • 2013-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多