admin管理员组

文章数量:1603247

名词解释:

对称加密:

对称加密就是通信双方使用同一把钥匙加密/解密信息,该方法的优点是加密过程简单,缺点是如何安全的将钥匙送到通讯双方手中。

非对称加密:

在非对称加密(典型算法RSA)中,加密和解密是采用不同的密钥,公钥是公开的,不需要保密,而私钥是由个人自己持有,公钥和私钥都能分别进行加密和解密。该方法的缺点是加密过程复杂,通讯效率低。

证书:

数字证书则是由证书认证机构(CA)对证书申请者真实身份验证之后,用CA的根证书对申请人的一些基本信息以及申请人的公钥进行签名(相当于加盖发证书机 构的公章)后形成的一个数字文件。CA完成签发证书后,会将证书发布在CA的证书库(目录服务器)中,任何人都可以查询和下载,因此数字证书和公钥一样是公开的。数字证书就是经过CA认证过的公钥。

自签名证书

证书除了由第三方私钥签名(比如由ca机构签名),也可以用自己的私钥签名。 用自己的私钥给自己的公钥签名的证书称为自签名证书。

加密通讯:

Alice想和Bob通讯,因此他俩互换公钥。想和谁通讯,就用对方的公钥加密。

   1、Alice用Bob的公开密钥加密她的消息,然后传送给Bob。

   2、Bob用他的私人密钥解密Alice的消息。

认证:

认证的目的是想鉴别用户的真伪,因此使用自己的私钥加密,只要自己的公钥能解开,就能证明自己的身份,这一过程被称为签名。想和谁认证,就用自己的私钥加密。

    1、Alice用她的私钥对文件加密,从而对文件签名。

    2、Alice将签名的文件传送给Bob。

    3、Bob用Alice的公钥解密文件,从而验证Alice的身份。

HTTPS通讯过程:

https采用混合加密方式(非对称加密+对称加密结合),它使用非对称加密传递对称加密的key,然后通讯双方用这把对称加密的key进行通讯。既解决了非对称加密复杂的问题,又解决了对称加密的钥匙传递问题。

但是这时通讯双方并不知道对方是不是真的对方,因此需要一个很有公信度的第三方来校验彼此(CA机构)

https七层网络模型如下表所示:

HTTPS(应用层)

(表示层)

SSL(会话层)

TCP(传输层)

IP(网络层)

以太网(数据链路层)

数据线/wifi(物理层)

Https是基于TCP协议的,因此整个通讯只建立一次连接,使用3次握手建立连接。

整个通讯步骤如下:(Alice为服务器,Bob为客户端)

1、Bob想和Alice通讯(客户端访问服务端),Bob向Alice发送hello、随机字符串B_String_1和自己支持的对称加密算法列表(ABCD……)(第一次握手)

2、Alice将自己的公钥发给CA,CA用自己的私钥加密(签名)后将其还给Alice。Alice就获得了CA认证的公钥,简称证书。

3、Alice收到B_String_1并保存下来,自己也生成一个随机字符串A_String_1、自己的CA签名公钥(证书)和选定的加密算法A,一并发给Bob。(第二次握手)

4、Bob会检验证书的真实性,首先与浏览器内置(Android则是操作系统内置)证书比对来确认这个公钥的真实性,比对成功后,Bob会用CA的公钥解密证书,就获得了Alice的公钥。

5、将A_String_1保存下来,并知晓使用A算法加密。Bob生成随机字符串B_String_2,并使用Alice的公钥加密对称B_String_2,发给Alice。(第三次握手)

6、Alice使用自己的私钥解密,获得B_String_2,至此双方都有B_String_1、A_String_1和B_String_2,以及知晓A算法了。

7、之后双方使用B_String_1+A_String_1+B_String_2生成对称密钥key,并用A算法进行加解密通讯。

Android请求https

Android在请求https时,会出现两种情况:

1、请求的服务器的公钥证书是由CA机构颁发的(例如www.baidu),系统就会帮我做证书校验,不需要额外的操作。

2、请求的服务器的公钥证书是非CA机构颁发的自签名证书(例如开发的服务器),就需要我们自己做证书校验(单向校验)。

1、CA签名

针对CA签名的服务器,不需要做特殊的校验,直接发起请求即可。

这里使用URLConnection、HttpsURLConnection甚至HttpURLConnection都可以

PS:因为HttpsURLConnection和HttpURLConnection继承自URLConnection,最终都是走URLConnection的openConnection方法。

log打印方法:

public class MyLog {

    private static final String TAG = "####";
    public static void log(InputStream inputStream){
        byte[] bytes = new byte[100];
        int flag=0;
        while (true){
            try {
                if (!((flag=inputStream.read(bytes))!=-1)) break;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            Log.i(TAG, new String(bytes));
        }
    }
}

具体代码:

public class Use_CA_Thread extends Thread{

    @Override
    public void run() {
        try {
            URL url = new URL("https://www.baidu/");
            URLConnection urlConnection = url.openConnection();
            urlConnection.connect();
            InputStream in = urlConnection.getInputStream();
            MyLog.log(in);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        super.run();
    }


}

访问成功:

若出现报错SSLHandshakeException,可能是测试机版本较低/时区错误/位置错误/配置了代理,需要更换高版本测试机/修改时区/修改位置/关闭代理。

2、自签名

对于自签名证书就需要做证书校验,或者做证书忽略才能访问。

证书校验:

Android应用需要先导入自签证书(.pem或.der),通常是放置在res/raw目录或者assets目录下,我这里是用charles的证书测的。

将证书文件读取为InputStream,使用CertificateFactory解析为Certificate对象。接着,创建一个KeyStore对象,并将证书放入该对象中。然后,通过TrustManagerFactory初始化信任管理器,并最终创建一个SSLContext对象,让HttpsURLConnection设置SSLContext。

具体代码如下:

public class Use_self_certificate extends Thread{


    Context context;

    public Use_self_certificate(Context context) {
        this.context = context;
    }

    @Override
    public void run() {
        super.run();

        try {
            //创建CertificateFactory
            //X.509为标准数字证书格式,所有的证书都符合X.509国标
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            //读取证书
            InputStream cainputStream=context.getResources().openRawResource(R.raw.chls);
            //通过CertificateFactory生成ca
            Certificate ca = certificateFactory.generateCertificate(cainputStream);
            cainputStream.close();
            //KeyStore有多种格式,JKS、BKS等,这里使用默认模式
            String keyStoreType = KeyStore.getDefaultType();
            //创建KeyStore
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            //加载keystore文件和密码,这里都为空
            keyStore.load(null,null);
            //设置ca
            keyStore.setCertificateEntry("ca",ca);
            //获取TrustManagerFactory算法名称
            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
            //创建TrustManager
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
            //TrustManager信任keystore
            trustManagerFactory.init(keyStore);
            //创建 SSLContext并使用TrustManager
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
            //创建HttpsURLConnection
            URL url = new URL("https://www.baidu");
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            //HttpsURLConnection设置SSLContext
            httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
            InputStream in = httpsURLConnection.getInputStream();
            MyLog.log(in);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

打印成功

若是报错SSLHandshakeException,可能是服务器与客户端不匹配

证书忽略

自定义TrustManager,让其不做验证,以达成证书忽略的目的。

代码如下:

public class Use_ignore_ca extends Thread{
    @Override
    public void run() {
        super.run();
        //自定义TrustManager[]对象
        TrustManager[] trustManagers = new TrustManager[]{
                //创建X509TrustManager对象
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        //不验证客户端
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        //不验证服务端
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        //直接返回
                        return new X509Certificate[0];
                    }
                }
        };

        try {
            SSLContext sslContext;
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null,trustManagers,null);
            URL url = new URL("https://www.baidu");
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            //HttpsURLConnection设置SSLContext
            httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
            InputStream in = httpsURLConnection.getInputStream();
            MyLog.log(in);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

打印成功

3、网络安全配置文件

使用一个 XML 文件来自定义网络安全配置,这样可以不需要修改代码,在manifest中进行添加,可以配置如下四种特征:

  • 自定义信任CA:针对应用的安全连接自定义哪些CA机构值得信赖。
  • 调试CA:在应用中以安全方式调试。
  • 停用明文流量:防止应用意外使用明文流量。
  • 证书固定:应用仅能连接到特定的证书。
    <?xml version="1.0" encoding="utf-8"?>
    <manifest ... >
        <application android:networkSecurityConfig="@xml/network_security_config"
                        ... >
            ...
        </application>
    </manifest>
    

自定义可信 CA

默认情况下,来自所有应用的安全连接(使用 TLS 和 HTTPS 之类的协议)均信任预装的系统 CA。

通过配置network_security_config.xml,使用 base-config(应用范围)或 domain-config(针对域名)自定义来配置信任域名和信任CA,证书格式为PEM或者DER。

base-config:信任系统证书和其他额外证书,额外证书存放在raw/extracas中。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/chls"/>
        </trust-anchors>
    </base-config>
</network-security-config>

domain-config:指定域名及指定CA

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <domain-config>
            <domain includeSubdomains="true">www.baidu</domain>
            <trust-anchors>
                <certificates src="@raw/chls"/>
            </trust-anchors>
        </domain-config>
    </network-security-config>
    

调试CA

使用 debug-overrides 指定仅在 android:debuggable 为 true 时才可信的仅调试 CA。

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <debug-overrides>
            <trust-anchors>
                <certificates src="@raw/chls"/>
            </trust-anchors>
        </debug-overrides>
    </network-security-config>
    

停用明文流量

设置针对某域名停用明文流量,可以禁止所有针对该域名的HTTP访问。

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <domain-config cleartextTrafficPermitted="false">
            <domain includeSubdomains="true">www.baidu</domain>
        </domain-config>
    </network-security-config>

固定证书

通过公钥的哈希值来匹配证书(X.509 证书的 SubjectPublicKeyInfo),以防止中间人攻击。固定证书时应包含一个备份密钥和证书到期时间,来防止证书更新等问题。

查看证书的hash

//在线提取百度证书
openssl s_client -connect www.baidu:443 -showcerts | openssl x509 -outform der > baidu.der
//提取公钥
openssl x509 -inform der -in baidu.der -pubkey -noout > baidu.pem
//提取证书sha256的base64格式
openssl dgst -sha256 -binary baidu.pem | openssl enc -base64
    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <domain-config>
            <domain includeSubdomains="true">https://www.baidu/</domain>
            <pin-set expiration="2023-12-19">
                <pin digest="SHA-256">MDVrY/WE2mxGTwXzTdBJtGvHawYRfuOC2Nb6ZeVhIQw=</pin>
                <!-- backup pin -->
                <pin digest="SHA-256">MDVrY/WE2mxGTwXzTdBJtGvHawYRfuOC2Nb6ZeVhIQw=</pin>
            </pin-set>
        </domain-config>
    </network-security-config>
    

本文标签: androidHTTPS