打印服务器证书信息:
openssl s_client -connect www.baidu.com:443
AndroidKeyStore创建RSA的Keypair,并使用它进行签名和验证:
查看PEM格式的证书信息:
openssl x509 -in certificate.pem -text -noout
查看DER格式的证书信息:
openssl x509 -in certificate.der -inform der -text -noout
创建双向认证的证书链:
1.产生公私钥对
openssl genrsa > root.key
openssl genrsa > server.key
openssl genrsa > client.key
2.生成根证书
openssl req -x509 -new -key root.key >root.crt
3.生成二级证书请求文件
openssl req -new -key server.key -out server.csr
openssl req -new -key client.key -out client.csr
4.为二级证书签名
openssl ca -in server.csr -cert root.crt -keyfile root.key -out ./server.crt
openssl ca -in client.csr -cert root.crt -keyfile root.key -out client.crt
此时会出错,解决方法:
mkdir -p ./demoCA/newcerts
touch ./demoCA/index.txt
touch ./demoCA/serial
echo '01' > ./demoCA/serial
cd ~
openssl rand -writerand .rnd
5.将客户端证书和密钥打包成pfx格式(可以导入浏览器)
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.pfx
6.将客户端密钥导出为PKCS8格式(可以导入AndroidKeyStore)
openssl pkcs8 -topk8 -inform PEM -in client.key -outform PEM -nocrypt > client.pem
Apache服务器端配置双向认证:
1.修改/usr/local/apache2/conf/httpd.conf
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf
2.修改/usr/local/apache2/conf/extra/httpd-ssl.conf
ServerName 192.168.169.227:443
SSLCertificateFile "/usr/local/apache2/conf/server.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
SSLCACertificateFile "/usr/local/apache2/conf/root.crt"
SSLVerifyClient require
SSLVerifyDepth 10
Android客户端实现双向认证:
public static void httpsDevPrivateKey() {
new Thread(new Runnable(){
@Override
public void run() {
URL url = null;
try {
// 证书格式设置为X.509,从浏览器下载的证书就是该格式,可以直接导入。
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// 加载https://192.168.169.227的服务器证书
InputStream caInput = new BufferedInputStream(new FileInputStream("/mnt/sdcard/227/server.crt"));
Certificate ca;
ca = cf.generateCertificate(caInput);
System.out.println("服务器证书信息:" + ((X509Certificate) ca).getSubjectDN());
caInput.close();
// 加载客户端证书,证书格式为X.509。
InputStream clientCaInput = new BufferedInputStream(new FileInputStream("/mnt/sdcard/client/client.crt"));
Certificate clientCa;
clientCa = cf.generateCertificate(clientCaInput);
System.out.println("客户端证书信息:" + ((X509Certificate) clientCa).getSubjectDN());
clientCaInput.close();
// 加载客户端私钥,格式为PKCS8。
// 注意:需要去掉-----BEGIN RSA PRIVATE KEY-----和-----END RSA PRIVATE KEY-----。
InputStream clientKeyInput = new BufferedInputStream(new FileInputStream("/mnt/sdcard/client/client.pem"));
byte[] clientKey = new byte[clientKeyInput.available()];
clientKeyInput.read(clientKey);
clientKeyInput.close();
// 解析BASE64编码中的数据。
clientKey = Base64.decode(clientKey, Base64.DEFAULT);
// 解析PKCS8格式的私钥。
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clientKey);
PrivateKey clientPrivateKey = keyFactory.generatePrivate(keySpec);
// 获取"AndroidKeyStore"。
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
keyStore.load(null, null);
// 导入服务器的证书到AndroidKeyStore。
keyStore.setCertificateEntry("test_ca", ca);
// 导入客户端私钥和证书到AndroidKeyStore。
keyStore.setKeyEntry("test_client", clientPrivateKey, null, new Certificate[]{clientCa});
// 使用AndroidKeyStore初始化TrustyManagerFactory。
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// 使用AndroidKeyStore初始化KeyManagerFactory。
String kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlgorithm);
kmf.init(keyStore, null);
// 用AndroidKeyStore初始化过的TrustManagerFactory和KeyManagerFactory初始化SSLContext。
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
url = new URL("https://192.168.169.227");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
// 因为SSL只支持域名的认证,不支持IP的认证,所以这里我们手动认证。
urlConnection.setHostnameVerifier(new HostnameVerifier()
{
@Override
public boolean verify(String hostname, SSLSession session)
{
if(hostname.equals("192.168.169.227")) {
return true;
} else {
return false;
}
}
});
// 将https的连接绑定到AndroidKeyStore上。
urlConnection.setSSLSocketFactory(context.getSocketFactory());
// HTTPS双向认证的方式网站的数据。
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
}).start();
}
private static void copyInputStreamToOutputStream(InputStream in, PrintStream out) throws IOException {
byte[] buffer = new byte[4096];
int readCount = 0;
while ((readCount = in.read(buffer)) > 0) {
out.write(buffer, 0, readCount);
}
}