头一次听说要做双向认证的时候一脸懵逼,不清楚啥玩意是双向认证,破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();
}
相关概念仍有问题的可以直接参考谷歌官方文档