草庐IT

java - Android - 如何以编程方式将证书存储在 keystore 中?

coder 2023-12-04 原文

我正在做一个金融交易安卓应用程序。它需要 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但无法成功。 (我想稍后在我的应用程序中使用此证书进行客户端身份验证) 我的问题是问。如何存储此证书以便它只能由我的应用程序访问?当用户注册到期时,我也可以删除这个证书。

请帮助并提前致谢。

最佳答案

  • 您的问题不在于 keystore 本身,而在于 您尝试存储新客户端的文件的位置 证书!
  • “RAW 文件夹”是您安装的应用程序包的一部分。所以 您可以“虚拟”访问它,并且只能读取,不能写入!
  • 如果您希望您的 keystore 是私有(private)的,您最好的选择是您的 application sandboxed-private-folder(内部存储).
    你不能在 RAW 文件夹中写入,但你可以在你的应用程序私有(private)文件夹中写入。
  • link您提供的存储/写入位置在 事实上私有(private)文件夹。所以它对你不起作用,因为你是 尝试“写入原始文件夹
  • 你可能已经知道了,但是你可以复制你的文件(R.raw.client) 从“原始文件夹”到您的应用程序私有(private)文件夹。这样,您只使用一个 keystore 文件(可读和可写)。

关于java - Android - 如何以编程方式将证书存储在 keystore 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33706984/

有关java - Android - 如何以编程方式将证书存储在 keystore 中?的更多相关文章

  1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  4. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

    question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  7. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  8. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  9. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  10. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

随机推荐