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

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

public class NetworkCallSecure extends AsyncTask { 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连接注册用户。 该过程是从用户获取详细信息并将其发送到服务器。 服务器将validation这些详细信息并在用户移动设备上发送确认PIN(在用户详细信息中获取此MSISDN)。 用户将输入此PIN,服务器将validationPIN是否相同。 用户validation后,客户端应用程序(用户移动)将生成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文件中存储证书。 我试着用另一种方式描述但不能成功。 (我想稍后在我的应用程序中使用此证书进行客户端身份validation)我的问题是问:如何存储此证书,以便只有我的应用程序才能访问它? 此外,我还可以在用户注册过期时删除此证书。

请提前帮助和感谢。

  • 您的问题不在于密钥库本身,而在于您尝试存储新客户端证书的文件的位置!
  • “RAW-folder”是已安装的应用程序包的一部分。 所以你可以“虚拟”访问它,只有READ,而不是WRITE!
  • 如果您希望密钥库是私有的,那么您的最佳选择是应用程序sandboxed-private-folder(内部存储)。
    您不能在RAW文件夹中写入,但可以在应用程序私有文件夹中写入。
  • 在您提供的链接中,存储/写入位置实际上是私人文件夹。 所以它不适合你,因为你试图“ 写入原始文件夹
  • 您可能已经知道了,但您可以将文件(R.raw.client)从“Raw-folder”复制到应用程序专用文件夹。 这样,您只使用一个密钥库文件(可读写)。