Android SSL证书固定

mtb9vblg  于 2023-10-19  发布在  Android
关注(0)|答案(5)|浏览(182)

我知道有很多问题关于钉在Android证书,但我找不到我要找的.
子类SSLSocketFactory并覆盖checkServerTrusted()方法。在此方法中,我执行以下操作:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(PUB_KEY.getBytes("UTF-8")));
for (X509Certificate cert : chain) {
    // Verifing by public key
    cert.verify(ca.getPublicKey());                      
}

链中的一个项验证,另一个不验证(抛出Exception)。我想我无法理解证书链是如何工作的。
同一个公共证书是否应该与链中的所有证书进行验证?

yks3o0rb

yks3o0rb1#

我发现在Android上实现证书固定的最简单方法是使用OkHttp库。
以下是文档的摘录:
默认情况下,OkHttp信任主机平台的证书颁发机构。这种策略最大限度地提高了连接性,但它会受到证书颁发机构的攻击,如2011 DigiNotar attack。它还假定HTTPS服务器的证书由证书颁发机构签名。
使用CertificatePinner来约束哪些证书颁发机构是可信的。证书锁定提高了安全性,但限制了您的服务器团队更新其TLS证书的能力。未经服务器TLS管理员许可,请勿使用证书固定!

public CertificatePinning() {
    client = new OkHttpClient();
    client.setCertificatePinner(
        new CertificatePinner.Builder()
            .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
            .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
            .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
            .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
            .build());
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/robots.txt")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    for (Certificate certificate : response.handshake().peerCertificates()) {
      System.out.println(CertificatePinner.pin(certificate));
    }
  }

如果您需要支持自签名证书,那么OkHttp是否支持接受自签名SSL证书?会指引你

3zwtqj6y

3zwtqj6y2#

您的疑问

同一个公共证书是否应该与链中的所有证书进行验证?
不能,因为链中的每个证书(根证书、中间证书和叶证书)都是用不同的私钥/公钥对签名的。
链中的一个项验证,另一个项不验证(抛出异常)。我想我无法理解证书链是如何工作的。
这是因为您的证书是叶子证书,因此您只能根据它来验证您的公钥,而不能根据根证书和中间证书来验证。

代码方法

子类SSLSocketFactory并覆盖checkServerTrusted()方法。
如果你真的想自己编写代码,我建议你使用内置的OkHttp Ceritficate Pinner,你可以像这样构建:

import okhttp3.CertificatePinner;

public class OkHttpPinnerService {

    // true if the Approov SDK initialized okay
    private boolean initialized;

    // cached OkHttpClient to use or null if not set
    private OkHttpClient okHttpClient;

    public synchronized OkHttpClient getOkHttpClient() {
        if (okHttpClient == null) {
            // build a new OkHttpClient on demand
            if (initialized) {
                // build the pinning configuration
                CertificatePinner.Builder pinBuilder = new CertificatePinner.Builder();
                Map<String, List<String>> pins = YourConfig.getPins("public-key-sha256");
                for (Map.Entry<String, List<String>> entry : pins.entrySet()) {
                    for (String pin : entry.getValue())
                        pinBuilder = pinBuilder.add(entry.getKey(), "sha256/" + pin);
                }

                // build the OkHttpClient with the correct pins preset and ApproovTokenInterceptor
                Log.i(TAG, "Building new Approov OkHttpClient");
                okHttpClient = okHttpBuilder.certificatePinner(pinBuilder.build()).build();
            } else {
                // if the Approov SDK could not be initialized then we can't pin or add Approov tokens
                Log.e(TAG, "Cannot build Approov OkHttpClient due to initialization failure");
                okHttpClient = okHttpBuilder.build();
            }
        }

        return okHttpClient;
    }    
}

代码没有测试语法错误或逻辑正确性。我只是从这个repo中复制了它,并稍微修改了一下。

无代码方式

由于Android API 24可以通过内置的安全配置文件实现证书固定到公钥哈希,这不需要编写任何代码,只需将正确配置的network_security_config.xml文件添加到项目中。
为了避免在构建network_security_config.xml文件时出现错误,我建议您使用Mobile Certificate Pinning Generator来提取您想要锁定的域所使用的live pin,并为您构建正确的配置。举例来说:

现在只需将生成的配置复制粘贴到项目中的network_security_config.xml文件中,并将同一文件添加到AndroifManifest.xml中。只要按照页面上的说明进行操作。

igetnqfo

igetnqfo3#

同一个公共证书是否应该与链中的所有证书进行验证?
答:没有。
大多数公共CA不直接签署服务器证书。相反,它们使用其主CA证书(称为root CA)来签署中间CA。他们这样做是为了使根CA可以离线存储,以降低泄露风险。然而,像Android这样的操作系统通常直接使用trust only root CAs,这在服务器证书(由中间CA签名)和证书验证器(知道根CA)之间留下了一个短的信任间隙。
为了解决这个问题,服务器在SSL握手期间不仅向客户端发送它的证书,而且还从服务器CA通过任何必要的中间机构向客户端发送证书链,以到达受信任的根CA。
查看此链接了解更多信息。希望这对用户有所帮助。

kgsdhlau

kgsdhlau4#

证书和公钥固定(也称为证书固定)简而言之-
通常,应用信任所有预安装的CA。如果这些CA中的任何一个颁发欺诈性证书,应用程序将面临中间人攻击(即窃听)的风险。某些应用选择通过限制它们信任的CA集或通过证书固定来限制它们接受的证书集。证书固定是通过公钥的哈希提供一组证书来完成的。证书固定是一种依赖于客户端的服务器证书验证的方法。
以下是在Android上实现证书固定的3种方法-

特别是对于您的问题,您可以使用<pin-set>标记在NSC中通过公钥的散列来配置证书。

  • 请注意,使用证书固定时,您应始终包含备份密钥,以便在强制切换到新密钥或更改CA(固定到CA证书或该CA的中间证书时)时,您的应用的连接性不受影响。否则,您必须向应用推送更新以恢复连接。*
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>
tvmytwxo

tvmytwxo5#

要在Kotlin Android应用中添加SSL固定,请按照以下分步说明操作:步骤1:将OkHttp库添加到项目中。将OkHttp依赖项添加到您的应用程序级build.gradle文件:

implementation 'com.squareup.okhttp3:okhttp:4.9.1'

步骤2:创建证书固定帮助程序类。在项目中创建一个新的Kotlin类,例如CertificatePinningHelper。此类将包含SSL固定的逻辑。下面是一个基本的实现:

import okhttp3.CertificatePinner
object CertificatePinningHelper {
    private const val HOSTNAME = "your-api-hostname.com"
private const val PIN = "sha256/YourPublicKeyHash"

fun getCertificatePinner(): CertificatePinner {
    return CertificatePinner.Builder()
        .add(HOSTNAME, PIN)
        .build()
}
}

将www.example.com替换your-api-hostname.com为要为其固定证书的API的实际主机名。将YourPublicKeyHash替换为服务器证书的实际公钥哈希。您可以使用OpenSSL或其他类似工具获取公钥哈希。
步骤3:使用CertificatePinner初始化OkHttpClient。在您的网络代码中,创建OkHttpClient的示例,并将其配置为使用在上一步中创建的CertificatePinner。下面是一个示例:

import okhttp3.OkHttpClient
val client = OkHttpClient.Builder()
.certificatePinner(CertificatePinningHelper.getCertificatePinner())
.build()

步骤4:使用OkHttpClient处理网络请求。使用已配置的OkHttpClient示例发出网络请求。举例来说:

import okhttp3.Request
val request = Request.Builder()
.url("https://your-api-hostname.com/api/endpoint")
.build()

client.newCall(request).enqueue(object : Callback {
  override fun onFailure(call: Call, e: IOException) {
    // Handle network request failure
}
    override fun onResponse(call: Call, response: Response) {
   // Handle network response

   }
})

确保将https://your-api-hostname.com/api/endpoint替换为您要访问的实际API端点。
就是这样!通过这些步骤,您已经使用OkHttp将SSL固定添加到了您的KotlinAndroid应用。

相关问题