【问题标题】:OkHttp Mutual SSL in AndroidAndroid中的OkHttp Mutual SSL
【发布时间】:2021-03-24 18:09:05
【问题描述】:

我正在尝试使用 okhttp 在我的 android 应用程序上实现双向 ssl。目前,我已经通过输入流从原始文件夹中读取服务器证书并将其添加到密钥库,将服务器证书添加到信任库,从而实现了证书锁定。我不清楚的是如何添加客户端密钥对以使用相互 ssl 初始化 okhttp 客户端。我当前的代码是;

        try {

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);
        InputStream certInputStream = context.getResources().openRawResource(
                context.getResources().getIdentifier("server",
                        "raw", context.getPackageName()));

        BufferedInputStream bis = new BufferedInputStream(certInputStream);
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        while (bis.available() > 0) {
            Certificate cert = certificateFactory.generateCertificate(bis);
            keyStore.setCertificateEntry("s1ha", cert);
        }
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        trustManagers = trustManagerFactory.getTrustManagers();
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagers, new SecureRandom());
    } catch (Exception e) {
        e.printStackTrace();
    }

我可以将带有客户端密钥对的密钥库复制到原始文件夹并打开该密钥库而不是默认类型并将其传递给信任管理器并构建 okhttp 客户端吗?

我是新手,对此我有点困惑。数周以来一直在寻找指南,但仍未找到明确解释该程序的指南。在这里找到的 Okhttp 自定义信任 okhttp 似乎很有希望,但我不确定它是否实现相互 SSL 任何帮助或示例代码将不胜感激。

【问题讨论】:

标签: android ssl okhttp android-security


【解决方案1】:

如果你可以在你的应用中加载证书那么你需要的代码大概是

    val keyPair: KeyPair = ...
    val certificate: X509Certificate = ...
    val intermediates: Array<X509Certificate> = ...

    val myClientCert = HeldCertificate(keyPair, certificate)
    
    val m = HandshakeCertificates.Builder()
      .addPlatformTrustedCertificates()
      .heldCertificate(myClientCert, *intermediates)
      .build()

    val client = OkHttpClient.Builder()
      .sslSocketFactory(m.sslSocketFactory(), m.trustManager)
      .build()

这会间接将其复制到临时密钥存储中,这就是您需要访问私钥字节的原因,从而降低了整体安全性。

【讨论】:

    【解决方案2】:

    如果您使用外部文件应用程序将证书加载到手机中,则可以使用批准的 Android KeyChain 进行设置,并让用户选择要使用的证书。这可能是最安全的选择。

    在这种模式下,您可以访问用于签名的私钥,但通常无法访问其字节或将其复制到外部。这就是为什么密钥由密钥管理器提供而不是复制到密钥库中的原因。

            KeyChain.choosePrivateKeyAlias(this,
                    { alias -> storeAlias(alias) },
                    arrayOf("RSA", "DSA"),  // List of acceptable key types. null for any
                    null,  // issuer, null for any
                    "server.cryptomix.com",  // host name of server requesting the cert, null if unavailable
                    443,  // port of server requesting the cert, -1 if unavailable
                    "mykey") // alias to preselect, null if unavailable
    ...
        private fun makeRequest(alias: String) {
                    val pk = KeyChain.getPrivateKey(applicationContext, alias)!!
                    val chain = KeyChain.getCertificateChain(applicationContext, alias)!!
    
                    val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
                    trustManagerFactory.init(null as KeyStore?)
                    val trustManagers = trustManagerFactory.trustManagers
    
                    val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
                    keyManagerFactory.init(null, null)
                    val keyManagers = keyManagerFactory.keyManagers
    
                    val km = object : X509KeyManager {
                        override fun getClientAliases(keyType: String?, issuers: Array<Principal>): Array<String> {
                            // TODO check issuers
                            return arrayOf(alias)
                        }
    
                        override fun chooseClientAlias(keyType: Array<out String>?, issuers: Array<out Principal>?, socket: Socket?): String {
                            // TODO check issuers and socket hostname against expected hosts to use client auth
                            return alias
                        }
    
                        override fun getServerAliases(keyType: String?, issuers: Array<Principal>): Array<String> {
                            return arrayOf()
                        }
    
                        override fun chooseServerAlias(keyType: String?, issuers: Array<Principal>, socket: Socket): String {
                            return ""
                        }
    
                        override fun getCertificateChain(alias: String?): Array<X509Certificate> {
                            return chain
                        }
    
                        override fun getPrivateKey(alias: String?): PrivateKey {
                            return pk
                        }
                    }
    
                    val sslContext = SSLContext.getInstance("TLS")
                    sslContext.init(arrayOf(km), trustManagers, null)
    
                    val client: OkHttpClient = OkHttpClient.Builder()
                            .sslSocketFactory(sslContext.socketFactory, trustManagers[0] as X509TrustManager)
                            .build()
    
                    val request: Request = Request.Builder()
                            .url("https://server.cryptomix.com/secure/")
                            .build()
                    try {
                        client.newCall(request).execute().use { response ->
                            val string = response.body!!.string()
                            println(string)
                        }
                    } catch (ioe: IOException) {
                        println(ioe)
                    }
                }
        }
    

    另见https://medium.com/@_kbremner/android-and-client-authentication-fd416af19a04

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-26
      • 2016-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多