【问题标题】:Android - How to store certificate in keystore programmatically?Android - 如何以编程方式将证书存储在密钥库中?
【发布时间】:2016-02-15 21:00:26
【问题描述】:

我正在制作一个金融交易安卓应用。它需要 SSL 身份验证,我成功地完成了它(Android 和 Tomcat 之间的握手)。我使用 keytool 和 openSSL 来生成服务器和客户端证书。 Tomcat 证书格式为 JKS,android 格式为 BKS。我将此 BKS 文件存储在 Raw 文件夹中,并按如下方式使用:

public class NetworkCallSecure extends AsyncTask<String, Void, String> {

ResponseListener responseListener;
Activity activity;
ResultCodes code;

public NetworkCallSecure(Activity activity, ResponseListener responseListener, ResultCodes code) {
    this.responseListener = responseListener;
    this.activity = activity;
    this.code = code;
}

@Override
protected String doInBackground(String... params) {

    try{

        System.setProperty("http.keepAlive", "false");
        HttpsURLConnection .setDefaultHostnameVerifier(new HostnameVerifier() {

                    public boolean verify(String hostname,
                                          SSLSession session) {
                        Log.d("HTTPS",hostname+":"+session);
                        return true;
                    }
                });

        char[] passwKey = "mypass".toCharArray();
        KeyStore ks = KeyStore.getInstance("BKS");
        InputStream in = activity.getResources().openRawResource(
                R.raw.client);
        InputStream is = activity.getResources().openRawResource(
                R.raw.client);
        ks.load(in, passwKey);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
        kmf.init(ks, passwKey);

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(kmf.getKeyManagers(),
                new X509TrustManager[] { new MyX509TrustManager(is,
                        passwKey) }, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());

        URL url = new URL(params[0]);

        HttpsURLConnection connection = (HttpsURLConnection) url
                .openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("Content-Length", "" + Integer.toString(params[1].getBytes().length));
        connection.setDoOutput(true);

        byte[] outputInBytes = params[1].getBytes("UTF-8");
        OutputStream os = connection.getOutputStream();
        os.write( outputInBytes );
        os.close();

        BufferedReader bin = new BufferedReader(new InputStreamReader(
                connection.getInputStream()));

        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = bin.readLine()) != null) {
            sb.append(line);
        }
        in.close();
        is.close();
        return sb.toString();
    } catch (Exception e) { // should never happen
        e.printStackTrace();
        Log.d("Err", e.toString());
    }
    return "no result";
}

@Override
protected void onPostExecute(String result) {
    responseListener.getResponse(result,code);
}
}

我的 Trustmanager 课程是:

public class MyX509TrustManager implements X509TrustManager {
X509TrustManager pkixTrustManager;

public MyX509TrustManager(InputStream trustStore, char[] password)
        throws Exception {
    // create a "default" JSSE X509TrustManager.

    KeyStore ks = KeyStore.getInstance("BKS");

    ks.load(trustStore, password);

    TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
    tmf.init(ks);

    TrustManager tms[] = tmf.getTrustManagers();

    /*
     * Iterate over the returned trustmanagers, look for an instance of
     * X509TrustManager. If found, use that as our "default" trust manager.
     */
    for (int i = 0; i < tms.length; i++) {
        if (tms[i] instanceof X509TrustManager) {
            pkixTrustManager = (X509TrustManager) tms[i];
            return;
        }
    }

    /*
     * Find some other way to initialize, or else we have to fail the
     * constructor.
     */
    throw new Exception("Couldn't initialize");
}

public void checkClientTrusted(X509Certificate[] arg0, String arg1)
        throws CertificateException {
    // TODO Auto-generated method stub
    try {
        pkixTrustManager.checkClientTrusted(arg0, arg1);
    } catch (CertificateException excep) {
        // do any special handling here, or rethrow exception.
    }

}

public void checkServerTrusted(X509Certificate[] arg0, String arg1)
        throws CertificateException {
    // TODO Auto-generated method stub
    try {
        pkixTrustManager.checkServerTrusted(arg0, arg1);
    } catch (CertificateException excep) {
        /*
         * Possibly pop up a dialog box asking whether to trust the cert
         * chain.
         */
    }
}

public X509Certificate[] getAcceptedIssuers() {
    // TODO Auto-generated method stub
    return pkixTrustManager.getAcceptedIssuers();
}
}

现在我想使用这个 HTTPS 连接注册用户。该过程是从用户那里获取详细信息并将其发送到服务器。服务器将验证这些详细信息并在用户手机上发送确认 PIN(在用户详细信息中获取此 MSISDN)。用户将输入此 PIN,服务器将验证 PIN 是否相同。验证用户后,客户端应用程序(用户移动)将生成 CSR 并将其发送到服务器。服务器将使用此 CSR 生成证书并将其发送给客户端(移动应用程序)。 现在我的问题是我想将此证书存储在只有我的应用可以访问此证书的位置。我正在尝试使用以下方法将其保存在我的 BKS 文件中的原始文件夹中:

private boolean storeCertInKeystore(byte[] cert) {
    try {
        InputStream is = getResources().openRawResource(
                R.raw.client);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream certstream = new ByteArrayInputStream(cert);
        X509Certificate certificate = (X509Certificate) cf.generateCertificate(certstream);
        KeyStore keyStore = KeyStore.getInstance("BKS");
        keyStore.load(is, "mypass".toCharArray());
        keyStore.setCertificateEntry("mycert", certificate);


        Log.d("My App Cert: ", "true");
        return true;
    } catch(Exception e) {
            e.printStackTrace();
    }
    return false;
}

此代码运行成功,但无法将证书存储在 BKS 文件中。我尝试了另一种方式描述here,但未能成功。 (我想稍后在我的应用程序中使用此证书进行客户端身份验证) 我的问题是问。如何存储此证书,以便只能由我的应用程序访问?我也可以在用户注册到期时删除这个证书。

请提前帮助和感谢。

【问题讨论】:

    标签: java android tomcat ssl https


    【解决方案1】:
    • 您的问题不在于密钥库本身,而在于 您尝试存储新客户端的文件的位置 证书!
    • “RAW 文件夹”是您安装的应用程序包的一部分。所以 您可以“虚拟”访问它,并且只能读取,不能写入!
    • 如果您希望您的密钥库是私有的,那么您最好的选择是您的 应用程序沙盒私有文件夹(内部存储)。
      您不能在 RAW 文件夹中写入,但您可以在应用程序私有文件夹中写入。
    • 在您提供的link 中,存储/写入位置位于 实际上是私人文件夹。所以它对你不起作用,因为你是 试图“写入原始文件夹
    • 你可能已经知道了,但是你可以复制你的文件(R.raw.client) 从“原始文件夹”到您的应用程序私有文件夹。这样,您只需使用一个密钥库文件(可读写)。

    【讨论】:

    • 您能否告诉我如何创建一个新的密钥库,然后将我的证书存储在该密钥库中以备后用。我将使用此证书进行 SSL 握手。
    猜你喜欢
    • 1970-01-01
    • 2011-06-27
    • 2017-12-10
    • 2013-03-30
    • 1970-01-01
    • 2012-01-14
    • 2020-02-01
    • 2011-11-09
    • 2014-03-13
    相关资源
    最近更新 更多