Android SSL单双向认证逻辑学习笔记

头一次听说要做双向认证的时候一脸懵逼,不清楚啥玩意是双向认证,破APP干嘛要认证,有啥重要信息吗,单向行不行,好吧你说双向就双向,赶紧一波学习。

先记录下啥是单向,啥是双向

  • 单向
    以Server认证为例,Server集成公钥私钥
    1.App发来SSL版本信息
    2.Server返回公钥,随机数
    3.App验证公钥没毛病,发送支持的对称加密算法
    4.Server挑选支持的对称加密算法,明文发给App
    5.App生成随机数,使用该算法加密信息,并使用Server给的公钥加密
    6.Server收到后私钥解密后得到对称加密数据,用协商好的对称加密算法解密得到原始数据
    7.Server返回数据,App使用公钥解密,使用协商好的对称加密方式解密得到原始数据

  • 双向
    一端完成证书校验后,单向认证再来一次,App以同样的方式校验Server信息

  • CA的作用
    完成第三步,校验证书合法性

有些运营会提供多个证书,刚开始有点懵逼,运营提供了一个公钥一个公钥一个中间件证书,公钥私钥好理解,直接套用SSL流程,中间件证书一段时间内懵逼,如果公钥私钥由官方认证的CA机构签发,那么中间件证书可以不要,Android集成主流CA公钥证书,HTTPS通信中会默认使用,之所有很多App还是集成中间件证书是因为用户可以手动关闭这些证书认证,集成中间件证书可以防止该类情况(设置搜索“信任的凭证“查看已有的证书)。

  • 逻辑
    CA -> 保证公钥有效性(是自己人不是贼人)
    公钥 -> 解开私钥加密数据,顺便用它加加密(是自己人加的密,不是贼人加的密)
    私钥 -> 解开公钥加密数据,顺便用它加加密 (确实是自己的公钥,不是贼人的公钥)
    所有的所有 -> 就是为了保证最终这个对称加密的方式、加密的数据绝对安全

上面提到第二步Server会向App发送他自己的公钥,这里可能被贼人窃取,虽然Server保证信任通过他自己的公钥加密后的数据,但是这人是谁~可能是隔壁老王,用他自己的公钥给App,用拦截下的公钥给Server写信(狗东西看了就看了还要改),为了防止隔壁老王窃取公钥伪装自己发消息,CA就有用了,再给公钥加个密,老王即使解了密,看了Server的公钥(玛德有被看了),但是,能伪装,能改吗,不能,因为老王没有CA的私钥,他即使还是牛逼到偷看了信息,但是已经没有机会再修改信息,如果用了自己的私钥加密给了App,App也解不开他那套,自然认证不过。那么问题来了,如果CA私钥也被老王窃取了怎么办???凉拌,这么牛的老王认命。

元素

  • 公钥
  • 私钥
  • 签名
  • 证书
 public SSLSocketFactory getSocketFactory(
            AssetManager assetManager,
            final String caCrtFile,
            final String crtFile,
            final String keyFile,
            final String password)
            throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        X509Certificate caCert = null;
        X509Certificate clientCert = null;

        BufferedInputStream caBis = new BufferedInputStream(assetManager.open(caCrtFile));
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        int i = 0;
        while (caBis.available() > 0) {
            caCert = (X509Certificate) certificateFactory.generateCertificate(caBis);
            Log.d("X509Certificate", "" + i++);
        }
        caBis = new BufferedInputStream(assetManager.open(crtFile));
        while (caBis.available() > 0) {
            clientCert = (X509Certificate) certificateFactory.generateCertificate(caBis);
        }
        // load client private key
        PEMParser pemParser = new PEMParser(new InputStreamReader(assetManager.open(keyFile)));
        Object object = pemParser.readObject();
        PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter()
                .setProvider("BC");
        KeyPair key;
        if (object instanceof PEMEncryptedKeyPair) {
            Log.e("TSPAPP", "Encrypted key - we will use provided password");
            key = converter.getKeyPair(((PEMEncryptedKeyPair) object)
                    .decryptKeyPair(decProv));
        } else {
            Log.e("TSPAPP", "Unencrypted key - no password needed");
            key = converter.getKeyPair((PEMKeyPair) object);
        }
        pemParser.close();

        KeyStore trustManagerFactoryKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        Log.d("hashcode 1", trustManagerFactoryKeyStore.hashCode()+"");

        trustManagerFactoryKeyStore.load(null, null);
        trustManagerFactoryKeyStore.setCertificateEntry("ca-certificate", caCert);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
        trustManagerFactory.init(trustManagerFactoryKeyStore);

        KeyStore keyManagerFactoryKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        Log.d("hashcode 2", keyManagerFactoryKeyStore.hashCode()+"");
        keyManagerFactoryKeyStore.load(null, null);
        keyManagerFactoryKeyStore.setCertificateEntry("certificate", clientCert);
        keyManagerFactoryKeyStore.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{clientCert});

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyManagerFactoryKeyStore, password.toCharArray());

        SSLContext context = SSLContext.getInstance("TLSv1.2");
        context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        return context.getSocketFactory();
    }

相关概念仍有问题的可以直接参考谷歌官方文档

你可能感兴趣的:(Android SSL单双向认证逻辑学习笔记)