如何使用Square OKHTTP固定证书?

 胖妞-Debbie_982 发布于 2023-01-12 07:12

我想我需要创建一个新的SSL套接字工厂?此外,出于显而易见的原因,我不想使用全局SSL上下文(https://github.com/square/okhttp/issues/184).

谢谢!

编辑:

从okhttp 2.1.0开始,您可以非常轻松地修复证书.

请参阅此处的源代码以开始使用

4 个回答
  • 这比OkHttp想象的要容易.

    跟着这些步骤:

    1.获取公共sha1密钥. 该OkHttp文档给了我们一个明确的方式来做到这一点完整的示例代码.万一它消失了,这里贴在下面:

    例如,要固定https://publicobject.com,请从破坏的配置开始:

    String hostname = "publicobject.com";
    CertificatePinner certificatePinner = new CertificatePinner.Builder()
        .add(hostname, "sha1/BOGUSPIN")
        .build();
    OkHttpClient client = new OkHttpClient();
    client.setCertificatePinner(certificatePinner);
    
    Request request = new Request.Builder()
        .url("https://" + hostname)
        .build();
    client.newCall(request).execute();   
    

    正如预期的那样,这会因证书固定异常而失败:

    javax.net.ssl.SSLPeerUnverifiedException:证书钉扎失败!
    对等证书链:sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw =:CN = publicobject.com,OU = PositiveSSL sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw =:CN = COMODO RSA域验证安全服务器CA sha1/blhOM3W9V/bVQhsWAcLYwPU6n24 =:CN = COMODO RSA证书颁发机构sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c =:CN = AddTrust外部CA根

    publicobject.com的固定证书:

    SHA1/BOGUSPIN
    在com.squareup.okhttp.CertificatePinner.check(CertificatePinner.java)
    在com.squareup.okhttp.Connection.upgradeToTls(Connection.java)
    在com.squareup.okhttp.Connection.connect(Connection.java)
    在玉米.squareup.okhttp.Connection.connectAndSetOwner(Connection.java)

    通过将异常中的公钥哈希粘贴到证书pinner的配置中来跟进:

    旁注:如果您在Android上执行此操作,如果您在UI线程上执行此操作,则会出现单独的异常,因此请确保在后台线程上执行此操作.

    2.配置OkHttp客户端:

    OkHttpClient 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());
    

    这里的所有都是它的!

    2023-01-12 07:21 回答
  • 如果您无法访问域(例如限制访问)并且无法测试虚假哈希,但是您有证书文件,则可以使用openssl来检索它:

    openssl x509 -in cert.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
    

    2023-01-12 07:21 回答
  • OKHTTP 3.0的更新

    OKHTTP 3.0 内置支持固定证书.首先粘贴以下代码:

     String hostname = "yourdomain.com";
     CertificatePinner certificatePinner = new CertificatePinner.Builder()
         .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
         .build();
     OkHttpClient client = OkHttpClient.Builder()
         .certificatePinner(certificatePinner)
         .build();
    
     Request request = new Request.Builder()
         .url("https://" + hostname)
         .build();
     client.newCall(request).execute();
    

    这将失败,因为AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA它不是您的证书的有效哈希.抛出的异常将具有您的证书的正确哈希:

     javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
       Peer certificate chain:
         sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=: CN=publicobject.com, OU=PositiveSSL
         sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=: CN=COMODO RSA Secure Server CA
         sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=: CN=COMODO RSA Certification Authority
         sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=: CN=AddTrust External CA Root
       Pinned certificates for publicobject.com:
         sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
       at okhttp3.CertificatePinner.check(CertificatePinner.java)
       at okhttp3.Connection.upgradeToTls(Connection.java)
       at okhttp3.Connection.connect(Connection.java)
       at okhttp3.Connection.connectAndSetOwner(Connection.java)
    

    确保将这些添加到CertificatePinner对象中,并且您已成功固定证书:

     CertificatePinner certificatePinner = new CertificatePinner.Builder()
       .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
       .add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
       .add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
       .add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
       .build();
    

    这里的所有东西都是OKHTTP的旧版本(2.x)版本

    阅读此博客文章后,我能够修改与OkHttp一起使用的概念.如果要避免使用全局SSL上下文,则应至少使用2.0版.

    此修改仅适用于OkHttp的当前实例,并更改该实例,以便它只接受来自指定证书的证书.如果您想要接受其他证书(例如来自Twitter的证书),您只需创建一个新的OkHttp实例,而不需要下面描述的修改.

    1.创建TrustStore

    为了固定证书,首先需要创建一个包含此证书的信任库.为了创建信任库,我们将使用nelenkov的这个方便的脚本为我们的目的稍作修改:

    #!/bin/bash
    
    if [ "$#" -ne 3 ]; then
      echo "Usage: importcert.sh <CA cert PEM file> <bouncy castle jar> <keystore pass>"
      exit 1
    fi
    
    CACERT=$1
    BCJAR=$2
    SECRET=$3
    
    TRUSTSTORE=mytruststore.bks
    ALIAS=`openssl x509 -inform PEM -subject_hash -noout -in $CACERT`
    
    if [ -f $TRUSTSTORE ]; then
        rm $TRUSTSTORE || exit 1
    fi
    
    echo "Adding certificate to $TRUSTSTORE..."
    keytool -import -v -trustcacerts -alias $ALIAS \
          -file $CACERT \
          -keystore $TRUSTSTORE -storetype BKS \
          -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
          -providerpath $BCJAR \
          -storepass $SECRET
    
    echo "" 
    echo "Added '$CACERT' with alias '$ALIAS' to $TRUSTSTORE..."
    

    要运行此脚本,您需要3件事:

      确保keytool(包含在Android SDK中)位于$ PATH上.

      确保您在与脚本相同的目录中下载最新的BouncyCastle jar文件.(在这里下载)

      你想要的证书.

    现在运行脚本

    ./gentruststore.sh your_cert.pem bcprov-jdk15on-150.jar your_secret_pass
    

    键入"是"以信任证书,并在完成mytruststore.bks时在您当前的目录中生成.

    2.将您的TrustStore应用于您的Android项目

    raw在您的res文件夹下创建一个目录.复制mytruststore.bks到这里.

    现在这是一个非常简单的类,将您的证书固定到OkHttp

    import android.content.Context;
    import android.util.Log;
    
    import com.squareup.okhttp.OkHttpClient;
    import com.squareup.okhttp.Request;
    import com.squareup.okhttp.Response;
    
    import java.io.InputStream;
    import java.io.Reader;
    import java.security.KeyStore;
    
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManagerFactory;
    
    
    /**
     * Created by martin on 02/06/14.
     */
    public class Pinning {
    
        Context context;
        public static String TRUST_STORE_PASSWORD = "your_secret";
        private static final String ENDPOINT = "https://api.yourdomain.com/";
    
        public Pinning(Context c) {
            this.context = c;
        }
    
        private SSLSocketFactory getPinnedCertSslSocketFactory(Context context) {
            try {
                KeyStore trusted = KeyStore.getInstance("BKS");
                InputStream in = context.getResources().openRawResource(R.raw.mytruststore);
                trusted.load(in, TRUST_STORE_PASSWORD.toCharArray());
                SSLContext sslContext = SSLContext.getInstance("TLS");
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                        TrustManagerFactory.getDefaultAlgorithm());
                trustManagerFactory.init(trusted);
                sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
                return sslContext.getSocketFactory();
            } catch (Exception e) {
                Log.e("MyApp", e.getMessage(), e);
            }
            return null;
        }
    
        public void makeRequest() {
            try {
                OkHttpClient client = new OkHttpClient();
                client.setSslSocketFactory(getPinnedCertSslSocketFactory(context));
    
                Request request = new Request.Builder()
                        .url(ENDPOINT)
                        .build();
    
                Response response = client.newCall(request).execute();
    
                Log.d("MyApp", response.body().string());
    
            } catch (Exception e) {
                Log.e("MyApp", e.getMessage(), e);
    
            }
        }
    }
    

    正如您所看到的,我们实例化了一个新的实例OkHttpClient和调用setSslSocketFactory,并SSLSocketFactory使用我们的自定义信任库传递.确保设置TRUST_STORE_PASSWORD为传递给shell脚本的密码.您的OkHttp实例现在应该只接受您指定的证书.

    2023-01-12 07:24 回答
  • 为了扩展示例源代码 @ Michael-barany共享,我做了一些测试,它似乎是一个误导性的代码示例.在示例中,异常注释的代码是来自证书链异常的4个sha1哈希:

    javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
    Peer certificate chain:
    sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=: CN=publicobject.com, OU=PositiveSSL
    sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=: CN=COMODO RSA Domain Validation Secure Server CA
    sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=: CN=COMODO RSA Certification Authority
    sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=: CN=AddTrust External CA Root
    

    然后将所有4个sha1公钥哈希添加到CertificatePinner Builder.

    CertificatePinner certificatePinner = 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();
    

    但是,给定测试我已经执行并查看代码,只会解释第一个有效的代码,因此您最适合仅包含返回的其中一个哈希值.您可以使用最具体的散列"DmxUShsZuNiqPQsX2Oi9uv2sCnw"来获取精确的站点证书...或者您可以根据所需的安全状态使用最宽泛的散列"T5x9IXmcrQ7YuQxXnxoCmeeQ84c"作为CA Root.

    2023-01-12 07:32 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有