62

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

4

9 回答 9

106

您可以使用 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 文件;它是纯文本,其中包含编码的未加密私钥和证书(采用非常明显的格式)。

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

于 2011-04-08T14:49:23.157 回答
32

最初来自 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 论坛上出现了导出私钥的问题, 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 包,这是一个“坏事”
如果你可以下载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
于 2008-09-29T19:12:01.997 回答
6

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

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

选择您的密钥库,选择私钥条目,然后选择 File->Save to apkcs12 文件(通常为 *.pfx)。然后,您可以使用以下命令查看内容:

$ openssl pkcs12 -in mykeyfile.pfx -info

于 2008-09-29T19:43:24.087 回答
5

这是上述代码的简短版本,在 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-----"
于 2012-02-01T23:32:48.167 回答
4

对于android开发,将eclipse ADT中创建的keystore转换为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
于 2011-11-29T09:59:05.680 回答
4

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

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

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

于 2014-06-18T13:54:03.013 回答
3

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

下面是一些简单的代码,可为您提供 OpenSSL 可以使用的未加密的 PKCS #8 PrivateKeyInfo(请参阅-nocryptpkcs8 实用程序的选项):

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 私钥的私钥并以您想要的格式输出。这将成为后续问题的一个很好的话题。

于 2008-09-29T19:16:08.257 回答
3

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

于 2015-02-02T09:54:51.670 回答
1

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

于 2012-07-13T09:31:02.237 回答