【问题标题】:How can I save my keystore securely如何安全地保存我的密钥库
【发布时间】:2016-01-25 14:13:16
【问题描述】:

我编写了一个 API,以便我的不同应用程序可以使用它。它只是检查应用程序是否已在服务器上注册。如果没有,则生成公钥/私钥对,并通过将 CSR 发送到我的服务器。下次它使用签名证书和私钥。最初我有原始的默认密钥库文件( b/c 我所有的通信都是通过 SSL 进行的,即最初使用默认密钥库并在注册用户生成的密钥库之后)

我的 API 工作正常。我遇到的问题在某种程度上与我缺乏知识或方法错误有关,因此我需要帮助。

我正在使用以下类来保存/检索我的密钥对

public class KeyIOHandler {


    public static void writePublicKeyToPreferences(KeyPair key, Context context) {
        StringWriter publicStringWriter = new StringWriter();
        try {
            PemWriter pemWriter = new PemWriter(publicStringWriter);
            pemWriter.writeObject(new PemObject("myapp.PUBLIC KEY", key.getPublic().getEncoded()));
            pemWriter.flush();
            pemWriter.close();
            SharedPreferences preferences = context.getSharedPreferences("SHARED_PREFERENCES",0);
            preferences.edit().putString("RSA_PUBLIC_KEY", publicStringWriter.toString()).commit();
            Log.e("Public  Key", publicStringWriter.toString());
        } catch (IOException e) {
            Log.e("RSA", e.getMessage());
            e.printStackTrace();
        }
    }

    public static void writePrivateKeyToPreferences(KeyPair keyPair, Context context) {
        StringWriter privateStringWriter = new StringWriter();
        try {
            PemWriter pemWriter = new PemWriter(privateStringWriter);
            pemWriter.writeObject(new PemObject("myapp.PRIVATE KEY", keyPair.getPrivate().getEncoded()));
            pemWriter.flush();
            pemWriter.close();
            SharedPreferences preferences = context.getSharedPreferences("SHARED_PREFERENCES",0);
            preferences.edit().putString("RSA_PRIVATE_KEY", privateStringWriter.toString()).commit();
            Log.e("Private Key",privateStringWriter.toString());
        } catch (IOException e) {
            Log.e("RSA", e.getMessage());
            e.printStackTrace();
        }
    }

    public static PublicKey getRSAPublicKeyFromString(String publicKeyPEM) throws Exception {
        publicKeyPEM = stripPublicKeyHeaders(publicKeyPEM);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SC");
        byte[] publicKeyBytes = Base64.decode(publicKeyPEM.getBytes("UTF-8"));
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
        return keyFactory.generatePublic(x509KeySpec);
    }

    public static PrivateKey getRSAPrivateKeyFromString(String privateKeyPEM) throws Exception {
        privateKeyPEM = stripPrivateKeyHeaders(privateKeyPEM);
        KeyFactory fact = KeyFactory.getInstance("RSA", "SC");
        byte[] clear = Base64.decode(privateKeyPEM);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clear);
        PrivateKey priv = fact.generatePrivate(keySpec);
        Arrays.fill(clear, (byte) 0);
        return priv;
    }

    public static String stripPublicKeyHeaders(String key) {
        //strip the headers from the key string
        StringBuilder strippedKey = new StringBuilder();
        String lines[] = key.split("\n");
        for (String line : lines) {
            if (!line.contains("BEGIN PUBLIC KEY") && !line.contains("END PUBLIC KEY") && !isNullOrEmpty(line.trim())) {
                strippedKey.append(line.trim());
            }
        }
        return strippedKey.toString().trim();
    }

    public static String stripPrivateKeyHeaders(String key) {
        StringBuilder strippedKey = new StringBuilder();
        String lines[] = key.split("\n");
        for (String line : lines) {
            if (!line.contains("BEGIN PRIVATE KEY") && !line.contains("END PRIVATE KEY") && !isNullOrEmpty(line.trim())) {
                strippedKey.append(line.trim());
            }
        }
        return strippedKey.toString().trim();
    }

    public static boolean isNullOrEmpty(String str) {
        return str == null || str.isEmpty();
    }
}

通过使用以下方法,我正在生成我的密钥库,但我很困惑如何以及在何处安全地存储此密钥库,以便我可以在应用程序的余生中使用它。(除非密钥被盗)。与共享首选项一样,我无法存储任何对象,所以在哪里保存这个密钥库。现在我的班级中有静态密钥库对象。(这是我知道的最糟糕的,但为了让它工作,我把它设为静态)

private boolean  addToStore(){
        try {
            Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
            clientPkcs12 = KeyStore.getInstance("PKCS12");
            clientPkcs12.load(null, null);
            clientPkcs12.setKeyEntry("clientCert", keyPair.getPrivate(), "123456".toCharArray(), chain);
        }
        catch(Exception ex){
            Log.e("",ex.getCause().getMessage());
        }
        return false;
    }

我浏览了几篇文章,其中有一篇很明显nelenkov.blogspot,但没有得到如何实现我的目标,或者我在保存密钥库时错了?我的意思是我每次都必须创建密钥库吗?

【问题讨论】:

    标签: java android ssl x509certificate keystore


    【解决方案1】:

    我不知道当您拥有Keystore 时,为什么要将您的凭据保留在共享首选项中。将密钥库文件加载到内存后,您可以随时添加/检索密钥。当你完成后,只需通过调用将其保存在文件中:

    FileOutputStream out = new FileOutputStream(keystoreFile);
        keystore.store(out, password);
        out.close();
    

    从文件加载:

    keystore.load(new FileInputStream(keystoreFile, password);
    

    @answer 发表评论

    您应该只使用Keystore。来自共享首选项的数据不安全,很容易从中检索私钥。您没有使用任何类型的加密,因此您的密钥存储在纯 PEM 文本中。但是,如果您使用Keystore,您的密钥受密码保护(在密钥级别和密钥库级别),因此如果有人在没有密码的情况下获得Keystore 文件,他将更难破解它。保存Keystore文件的最佳位置是您的应用程序内部空间磁盘,没有root手机无法访问(创建文件时Context.MODE_PRIVATE)。

    【讨论】:

    • 谢谢回复,我已经提到可能是我弄错了,所以正如你所说,我有两件事要问,1#我是否需要将我的密钥保存在 SP 中正在做还是只有密钥库就足够了? 2# 把这个文件保存在哪里,这样别人就不能访问了?
    • thnx 再次,用这个测试,一旦我成功就会接受你的答案
    • Nikolay Elenkov 博客是有关 Android 安全性的所有信息的重要来源。我建议浏览他所有的博客文章,以获取大量相关知识。您也可以查看他的书:nelenkov.blogspot.com/2014/04/android-security-internals.html
    【解决方案2】:

    您已经回答了自己的问题。将它们保存在KeyStore 中,不是在首选项文件中。

    【讨论】:

      猜你喜欢
      • 2015-11-24
      • 1970-01-01
      • 2015-03-17
      • 2014-10-18
      • 2015-07-04
      • 2018-08-27
      • 2023-03-13
      • 1970-01-01
      相关资源
      最近更新 更多