简体   繁体   English

Android - 如何以编程方式在密钥库中存储证书?

[英]Android - How to store certificate in keystore programmatically?

I am making a financial transaction android app. 我正在制作一个金融交易Android应用程序。 It requires SSL authentication and I successfully able to complete it(handshake between Android and Tomcat). 它需要SSL身份验证,我成功完成了它(Android和Tomcat之间的握手)。 I used keytool and openSSL to generate Server and client certificates. 我使用keytool和openSSL生成服务器和客户端证书。 Tomcat certifcate format is JKS and android formate is BKS. Tomcat certifcate格式是JKS,而android formate是BKS。 I stored this BKS file in Raw folder and use this as follows: 我将此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);
}
}

My Trustmanager class is: 我的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();
}
}

Now I want to register user using this HTTPS connection. 现在我想使用此HTTPS连接注册用户。 The process is get details from user and send it to server. 该过程是从用户获取详细信息并将其发送到服务器。 Server will verify these details and send confirmation PIN on user mobile (got this MSISDN in user details). 服务器将验证这些详细信息并在用户移动设备上发送确认PIN(在用户详细信息中获取此MSISDN)。 User will enter this PIN and server will verify that PIN is same. 用户将输入此PIN,服务器将验证PIN是否相同。 After user is verified client app (user mobile) will generate a CSR and send it to server. 用户验证后,客户端应用程序(用户移动)将生成CSR并将其发送到服务器。 Server will generate Certificate using this CSRand send it to client (mobile app). 服务器将使用此CSR生成证书并将其发送到客户端(移动应用程序)。 Now my problem is I want to store this certificate where only my App can access this certificate. 现在我的问题是我想存储此证书,只有我的应用程序可以访问此证书。 I am trying to save this in my BKS file in raw folder using this: 我试图将此保存在我的原始文件夹中的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;
}

This code runs successfully but could not store cert in BKS file. 此代码成功运行,但无法在BKS文件中存储证书。 I tried another way describe here but could not succeed. 我尝试另一种方式描述了这里 ,但未能成功。 (I want to use this certificate later in my app for client authentication) My Question is Q. How can I store this certificate so it can be only accessible by my app? (我想稍后在我的应用程序中使用此证书进行客户端身份验证)我的问题是问:如何存储此证书,以便只能通过我的应用程序访问? And also I can delete this certificate when user registration expires. 此外,我还可以在用户注册过期时删除此证书。

Please help and thanks in advance. 请提前帮助和感谢。

  • Your issue is not with the keystore itself, but rather with the location of the file where you're trying to store the new client certificate! 您的问题不在于密钥库本身,而在于您尝试存储新客户端证书的文件的位置!
  • The "RAW-folder" is part of your installed application package. “RAW-folder”是已安装的应用程序包的一部分。 So you can "virtually" access it, and only READ, not WRITE! 所以你可以“虚拟”访问它,只读,而不是写!
  • Your best option, if you want your keystore to be private, is your application sandboxed-private-folder(Internal storage). 如果您希望密钥库是私有的,那么您的最佳选择是应用程序sandboxed-private-folder(内部存储)。
    You can't write in the RAW-folder, but you can write in your application private folder. 您不能在RAW文件夹中写入,但可以在应用程序私有文件夹中写入。
  • In the link that you provided, the storage/write location is in fact the private folder. 在您提供的链接中,存储/写入位置实际上是私人文件夹。 So it didn't work for you because you're trying to " write in the Raw-Folder " 所以它不适合你,因为你试图“ 写入原始文件夹
  • You probably know that already, but you can copy your file(R.raw.client) from the "Raw-folder" to your application private folder. 您可能已经知道了,但您可以将文件(R.raw.client)从“Raw-folder”复制到应用程序专用文件夹。 That way, you only use one keystore file(readable and writable). 这样,您只使用一个密钥库文件(可读写)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM