【问题标题】:How do I list / export private keys from a keystore?如何从密钥库中列出/导出私钥?
【发布时间】:2008-09-29 19:07:49
【问题描述】:

如何列出和导出密钥库中的私钥?

【问题讨论】:

    标签: java ssl keystore


    【解决方案1】:

    您可以使用 Java6 和 OpenSSL 从密钥库中提取私钥。这一切都取决于 Java 和 OpenSSL 都支持 PKCS#12 格式的密钥库这一事实。要进行提取,首先使用keytool 转换为标准格式。确保对两个文件使用相同的密码(私钥密码,而不是密钥库密码),否则您稍后会在第二步中遇到奇怪的失败。

    keytool -importkeystore -srckeystore keystore.jks \
        -destkeystore intermediate.p12 -deststoretype PKCS12
    

    接下来,使用 OpenSSL 提取到 PEM:

    openssl pkcs12 -in intermediate.p12 -out extracted.pem -nodes
    

    您应该能够足够轻松地处理该 PEM 文件;它是纯文本,其中包含编码的未加密私钥和证书(采用非常明显的格式)。

    执行此操作时,请注意确保创建的文件安全。它们包含秘密凭据。如果您未能正确保护它们,则不会发出任何警告。 保护它们的最简单方法是在除用户之外的任何人都没有任何访问权限的目录中执行所有这些操作。并且永远不要将您的密码放在命令行或环境变量中;其他用户太容易抢到了。

    【讨论】:

    • 背景故事:今天需要这个,所以我们可以将wireshark放在Java webservice客户端和Java webservice服务器之间;它像梦一样工作,我们修复了我们的错误(cookie 处理中的配置问题)。我想我会分享,因为不必编写任何代码来做这类事情很方便,而且很多人已经安装了所需的工具,这与其他答案不同。
    • “确保你对两个文件使用相同的密码 [...] 否则你会遇到奇怪的失败”我想我爱你! ——对我来说,新存储可以有不同的密码是有道理的。如果 keytool 无法处理不同的密码,为什么它会提示输入两个不同的密码,这超出了我的理解。
    • @Ape-in​​ago 不知道;它增加了很多复杂性,只是为了最边际的安全利益,因为任何合法使用最终都将不得不处理密码管理,而这通常是实际密码系统的弱点所在。 “让我们揭露它,因为我们可以!”似乎是许多加密库的一个功能,这可能就是为什么它们几乎普遍享有可怕的名声……嗯。
    【解决方案2】:

    最初来自 Example Depot 的部分代码用于列出密钥库中的所有别名:

        // Load input stream into keystore
        keystore.load(is, password.toCharArray());
    
        // List the aliases
        Enumeration aliases = keystore.aliases();
        for (; aliases.hasMoreElements(); ) {
            String alias = (String)aliases.nextElement();
    
            // Does alias refer to a private key?
            boolean b = keystore.isKeyEntry(alias);
    
            // Does alias refer to a trusted certificate?
            b = keystore.isCertificateEntry(alias);
        }
    

    几个月前Sun forums 提出了私钥的导出,u:turingcompleter 提出了一个 DumpPrivateKey 类来拼接到您的应用中。

    import java.io.FileInputStream;
    import java.security.Key;
    import java.security.KeyStore;
    import sun.misc.BASE64Encoder;
    
    public class DumpPrivateKey {
         /**
         * Provides the missing functionality of keytool
         * that Apache needs for SSLCertificateKeyFile.
         *
         * @param args  <ul>
         *              <li> [0] Keystore filename.
         *              <li> [1] Keystore password.
         *              <li> [2] alias
         *              </ul>
         */
        static public void main(String[] args)
        throws Exception {
            if(args.length < 3) {
              throw new IllegalArgumentException("expected args: Keystore filename, Keystore password, alias, <key password: default same tha
    n keystore");
            }
            final String keystoreName = args[0];
            final String keystorePassword = args[1];
            final String alias = args[2];
            final String keyPassword = getKeyPassword(args,keystorePassword);
            KeyStore ks = KeyStore.getInstance("jks");
            ks.load(new FileInputStream(keystoreName), keystorePassword.toCharArray());
            Key key = ks.getKey(alias, keyPassword.toCharArray());
            String b64 = new BASE64Encoder().encode(key.getEncoded());
            System.out.println("-----BEGIN PRIVATE KEY-----");
            System.out.println(b64);
            System.out.println("-----END PRIVATE KEY-----");
        }
        private static String getKeyPassword(final String[] args, final String keystorePassword)
        {
           String keyPassword = keystorePassword; // default case
           if(args.length == 4) {
             keyPassword = args[3];
           }
           return keyPassword;
        }
    }
    

    注意:这里使用 Sun 包,which is a "bad thing".
    如果你可以下载apache commons code,这里有一个可以编译没有警告的版本:

    javac -classpath .:commons-codec-1.4/commons-codec-1.4.jar DumpPrivateKey.java
    

    会给出相同的结果:

    import java.io.FileInputStream;
    import java.security.Key;
    import java.security.KeyStore;
    //import sun.misc.BASE64Encoder;
    import org.apache.commons.codec.binary.Base64;
    
    public class DumpPrivateKey {
         /**
         * Provides the missing functionality of keytool
         * that Apache needs for SSLCertificateKeyFile.
         *
         * @param args  <ul>
         *              <li> [0] Keystore filename.
         *              <li> [1] Keystore password.
         *              <li> [2] alias
         *              </ul>
         */
        static public void main(String[] args)
        throws Exception {
            if(args.length < 3) {
              throw new IllegalArgumentException("expected args: Keystore filename, Keystore password, alias, <key password: default same tha
    n keystore");
            }
            final String keystoreName = args[0];
            final String keystorePassword = args[1];
            final String alias = args[2];
            final String keyPassword = getKeyPassword(args,keystorePassword);
            KeyStore ks = KeyStore.getInstance("jks");
            ks.load(new FileInputStream(keystoreName), keystorePassword.toCharArray());
            Key key = ks.getKey(alias, keyPassword.toCharArray());
            //String b64 = new BASE64Encoder().encode(key.getEncoded());
            String b64 = new String(Base64.encodeBase64(key.getEncoded(),true));
            System.out.println("-----BEGIN PRIVATE KEY-----");
            System.out.println(b64);
            System.out.println("-----END PRIVATE KEY-----");
        }
        private static String getKeyPassword(final String[] args, final String keystorePassword)
        {
           String keyPassword = keystorePassword; // default case
           if(args.length == 4) {
             keyPassword = args[3];
           }
           return keyPassword;
        }
    }
    

    你可以这样使用它:

    java -classpath .:commons-codec-1.4/commons-codec-1.4.jar DumpPrivateKey $HOME/.keystore changeit tomcat
    

    【讨论】:

    • 这很好用。我有一个客户使用 tomcat 指令创建了一个证书请求,因此私钥安装在他们的密钥库中。我需要它来使用 apache。
    • 一年多之后,你解决了别人的问题(即我的)。做得好! :)
    • 实际上,这应该用一个额外的参数来更新:[3] 密钥密码和行Key key = ks.getKey(args[2], args[1].toCharArray());应该是Key key = ks.getKey(args[2], args[3].toCharArray());。原始代码假定 KEY 密码与 KEYSTORE 密码相同,但它们不一定相同。
    • @REW:我已经编辑了答案以包含更强大的密钥转储器版本。
    • 如何列出别名的链接现在显然已经死了。不确定它实际上包含什么,因此无法找到合适的替代品。
    【解决方案3】:

    如果您不需要以编程方式执行此操作,而只是想管理您的密钥,那么我已经使用 IBM 的免费 KeyMan 工具很长时间了。非常适合将私钥导出到 PFX 文件(然后您可以轻松地使用 OpenSSL 对其进行操作、提取、更改密码等)。

    https://www.ibm.com/developerworks/mydeveloperworks/groups/service/html/communityview?communityUuid=6fb00498-f6ea-4f65-bf0c-adc5bd0c5fcc

    选择您的密钥库,选择私钥条目,然后选择文件->保存到 pkcs12 文件(通常为 *.pfx)。然后您可以通过以下方式查看内容:

    $ openssl pkcs12 -in mykeyfile.pfx -info

    【讨论】:

    • KeyMan 页面链接已更改为 ibm.com/developerworks/mydeveloperworks/groups/service/html/…(永远不会结束 url 404/重写!)
    • 下载链接在页面右上角。您需要一个 IBM ID……您可以免费创建一个。我选择下载第一个 zip 选项。要使其在 Windows 上运行,您需要解压缩到 C:\Program Files\IBM\BlueZ\KeyMan,或者您需要解压缩到任何您想要的位置并更改 km.bat 的第一行以获取该文件夹的路径包含km.bat。然后只运行 km.bat 而不带任何参数。只要 java 在您的路径上就应该可以工作。
    【解决方案4】:

    这是上述代码的简短版本,在 Groovy 中。还内置base64编码:

    import java.security.Key
    import java.security.KeyStore
    
    if (args.length < 3)
            throw new IllegalArgumentException('Expected args: <Keystore file> <Keystore format> <Keystore password> <alias> <key password>')
    
    def keystoreName = args[0]
    def keystoreFormat = args[1]
    def keystorePassword = args[2]
    def alias = args[3]
    def keyPassword = args[4]
    
    def keystore = KeyStore.getInstance(keystoreFormat)
    keystore.load(new FileInputStream(keystoreName), keystorePassword.toCharArray())
    def key = keystore.getKey(alias, keyPassword.toCharArray())
    
    println "-----BEGIN PRIVATE KEY-----"
    println key.getEncoded().encodeBase64()
    println "-----END PRIVATE KEY-----"
    

    【讨论】:

      【解决方案5】:

      对于安卓开发, 将 eclipse ADT 中创建的密钥库转换为 SignApk.jar 中使用的公钥和私钥:

      导出私钥:

      keytool.exe -importkeystore -srcstoretype JKS -srckeystore my-release-key.keystore -deststoretype PKCS12 -destkeystore keys.pk12.der
      openssl.exe pkcs12 -in keys.pk12.der -nodes -out private.rsa.pem
      

      编辑 private.rsa.pem 并将 "-----BEGIN PRIVATE KEY-----" 保留为 "-----END PRIVATE KEY-----" 段落,然后:

      openssl.exe base64 -d -in private.rsa.pem -out private.rsa.der
      

      导出公钥:

      keytool.exe -exportcert -keystore my-release-key.keystore -storepass <KEYSTORE_PASSWORD> -alias alias_name -file public.x509.der
      

      签署apk:

      java -jar SignApk.jar public.x509.der private.rsa.der input.apk output.apk
      

      【讨论】:

      • 我尝试了你的建议,但是在第一个 openssl 命令时它失败了——它报告了解密错误。我测试的原始密钥库是与 jarsigner 一起使用的例程。失败发生在 cygwin 的工具和 Fedora 的工具下。
      • 它对我有用,我在网上搜索了这个解决方案,感谢 diyism
      【解决方案6】:

      这个问题出现在 stackexchange 安全性上,其中一个建议是使用 Keystore explorer

      https://security.stackexchange.com/questions/3779/how-can-i-export-my-private-key-from-a-java-keytool-keystore

      刚刚尝试过,效果非常好,强烈推荐。

      【讨论】:

        【解决方案7】:

        首先,要小心!您所有的安全都取决于……呃……私钥的privacy Keytool 没有内置密钥导出功能,以避免意外泄露这些敏感材料,因此您可能需要考虑采取一些额外的保护措施来保护您导出的密钥。

        这里有一些简单的代码,可以为您提供 OpenSSL 可以使用的未加密的 PKCS #8 PrivateKeyInfo(请参阅其pkcs8 utility-nocrypt 选项):

        KeyStore keys = ...
        char[] password = ...
        Enumeration<String> aliases = keys.aliases();
        while (aliases.hasMoreElements()) {
          String alias = aliases.nextElement();
          if (!keys.isKeyEntry(alias))
            continue;
          Key key = keys.getKey(alias, password);
          if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) {
            /* Most PrivateKeys use this format, but check for safety. */
            try (FileOutputStream os = new FileOutputStream(alias + ".key")) {
              os.write(key.getEncoded());
              os.flush();
            }
          }
        }
        

        如果您需要其他格式,可以使用 KeyFactory 为不同类型的密钥获取透明的密钥规范。然后,您可以获得例如 RSA 私钥的私有指数,并以您想要的格式输出。这将是一个很好的后续问题主题。

        【讨论】:

          【解决方案8】:

          另一个很棒的工具是 KeyStore Explorer:http://keystore-explorer.sourceforge.net/

          【讨论】:

            【解决方案9】:

            另一种不太传统但可以说更简单的方法是使用JXplorer。尽管此工具旨在浏览 LDAP 目录,但它具有用于操作密钥库的易于使用的 GUI。 GUI 上的一个这样的功能可以从 JKS 密钥库中导出私钥。

            【讨论】:

            • 因为我不知何故得到了错误。也许是因为我的密钥库可以追溯到 ....1900 年左右。导出的唯一方法是使用 JXplorer。谢谢! :)
            猜你喜欢
            • 2011-02-08
            • 1970-01-01
            • 1970-01-01
            • 2013-08-06
            • 2014-05-30
            • 2022-01-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多