一、HTTPS和HTTP的区别
1
、
https
协议需要到
ca
申请证书,一般免费证书很少,需要交费。
2
、
http
是超文本传输协议,信息是明文传输,
https
则是具有安全性的
ssl
加密传输协议。
3
、
http
和
https
使用的是完全不同的连接方式,用的端口也不一样,前者是
80
,后者是
443
。
4
、
http
的连接很简单,是无状态的;
HTTPS
协议是由
SSL+HTTP
协议构建的可进行加密传输、身份认证的网络协议,比
http
协议安全。
二、SSL功能
1)
客户对服务器的身份认证
:
SSL
服务器允许客户的浏览器使用标准的公钥加密技术和一些可靠的认证中心(
CA
)的证书,来确认服务器的合法性。
2)
服务器对客户的身份认证
:
也可通过公钥技术和证书进行认证,也可通过用户名,
password
来认证。
3)
建立服务器与客户之间安全的数据通道
:
SSL
要求客户与服务器之间的所有发送的数据都被发送端加密、接收端解密,同时还检查数据的完整性。
SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层:
SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
三、生成密钥库和证书
可参考以下密钥生成脚本,根据实际情况做必要的修改,其中需要注意的是:服务端的密钥库参数“CN”必须与服务端的IP地址相同,否则会报错,客户端的任意。
1、生成服务器证书库
keytool-validity365-genkey-v-aliasserver-keyalgRSA-keystoreD:\ssl\server.keystore-dname"CN=127.0.0.1,OU=rongyiwang,O=rongyiwang,L=Shanghai,ST=Shanghai,c=cn"-storepass123456-keypass123456
2、生成客户端证书库
keytool-validity365-genkeypair-v-aliasclient-keyalgRSA-storetypePKCS12-keystoreD:\ssl\client.p12-dname"CN=client,OU=rongyiwang,O=rongyiwang,L=Shanghai,ST=Shanghai,c=cn"-storepass123456-keypass123456
3、从客户端证书库中导出客户端证书
keytool-export-v-aliasclient-keystoreD:\ssl\client.p12-storetypePKCS12-storepass123456-rfc-fileD:\ssl\client.cer
4、从服务器证书库中导出服务器证书
keytool-export-v-aliasserver-keystoreD:\ssl\server.keystore-storepass123456-rfc-fileD:\ssl\server.cer
5、生成客户端信任证书库(由服务端证书生成的证书库)
keytool-import-v-aliasserver-fileD:\ssl\server.cer-keystoreD:\ssl\client.truststore-storepass123456
6、将客户端证书导入到服务器证书库(使得服务器信任客户端证书)
keytool-import-v-aliasclient-fileD:\ssl\client.cer-keystoreD:\ssl\server.keystore-storepass123456
7、查看证书库中的全部证书
keytool-list-keystoreD:\ssl\server.keystore-storepass123456
通过上面的步骤生成的证书,客户端需要用到的是client.p12(客户端证书,用于请求的时候给服务器来验证身份之用)和client.truststore(客户端证书库,用于验证服务器端身份,防止钓鱼)这两个文件.
其中安卓端的证书类型必须要求是BKS类型,具体生成可以参考这个createa bks bouncycastle,这里涉及到这个JARbcprov-ext-jdk15on-152.jar文件.
以下只给出Android端的请求,具体服务器端的配置可以参考这篇博客-->JavaTomcat SSL 服务端/客户端双向认证(一)
四、Android端SSL认证请求
一般客户端验证SSL有两种方式,一种是通过SSLSocketFactory方式创建,需要设置域名及端口号(适应于HttpClient请求方式),一种是通过SSLContext方式创建(适用于HttpsURLConnection请求方式).
1、下面给出SslSocketFactory方式进行SSL认证的客户端代码
privatestaticfinalStringKEY_STORE_TYPE_BKS="bks";//证书类型固定值
privatestaticfinalStringKEY_STORE_TYPE_P12="PKCS12";//证书类型固定值
privatestaticfinalStringKEY_STORE_CLIENT_PATH="client.p12";//客户端要给服务器端认证的证书
privatestaticfinalStringKEY_STORE_TRUST_PATH="client.truststore";//客户端验证服务器端的证书库
privatestaticfinalStringKEY_STORE_PASSWORD="123456";//客户端证书密码
privatestaticfinalStringKEY_STORE_TRUST_PASSWORD="123456";//客户端证书库密码
/**
* 获取SslSocketFactory
*
* @param context 上下文
* @return SSLSocketFactory
*/
publicstaticSSLSocketFactorygetSslSocketFactory(Contextcontext){
try{
//服务器端需要验证的客户端证书
KeyStorekeyStore=KeyStore.getInstance(KEY_STORE_TYPE_P12);
//客户端信任的服务器端证书
KeyStoretrustStore=KeyStore.getInstance(KEY_STORE_TYPE_BKS);
InputStreamksIn=context.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);
InputStreamtsIn=context.getResources().getAssets().open(KEY_STORE_TRUST_PATH);
try{
keyStore.load(ksIn,KEY_STORE_PASSWORD.toCharArray());
trustStore.load(tsIn,KEY_STORE_TRUST_PASSWORD.toCharArray());
}catch(Exceptione){
e.printStackTrace();
}finally{
try{
ksIn.close();
}catch(Exceptionignore){
}
try{
tsIn.close();
}catch(Exceptionignore){
}
}
returnnewSSLSocketFactory(keyStore,KEY_STORE_PASSWORD,trustStore);
}catch(KeyManagementException|UnrecoverableKeyException|KeyStoreException|FileNotFoundException|NoSuchAlgorithmException|ClientProtocolExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
returnnull;
}
/**
* 获取SSL认证需要的HttpClient
*
* @param context 上下文
* @param port 端口号
* @return HttpClient
*/
publicstaticHttpClientgetSslSocketFactoryHttp(Contextcontext,intport){
HttpClienthttpsClient=newDefaultHttpClient();
SSLSocketFactorysslSocketFactory=getSslSocketFactory(context);
if(sslSocketFactory!=null){
Schemesch=newScheme("https",sslSocketFactory,port);
httpsClient.getConnectionManager().getSchemeRegistry().register(sch);
}
returnhttpsClient;
}
2、下面给出SSLContext方式进行SSL认证的客户端代码
privatestaticfinalStringKEY_STORE_TYPE_BKS="bks";//证书类型固定值
privatestaticfinalStringKEY_STORE_TYPE_P12="PKCS12";//证书类型固定值
privatestaticfinalStringKEY_STORE_CLIENT_PATH="client.p12";//客户端要给服务器端认证的证书
privatestaticfinalStringKEY_STORE_TRUST_PATH="client.truststore";//客户端验证服务器端的证书库
privatestaticfinalStringKEY_STORE_PASSWORD="123456";//客户端证书密码
privatestaticfinalStringKEY_STORE_TRUST_PASSWORD="123456";//客户端证书库密码
/**
* 获取SSLContext
*
* @param context 上下文
* @return SSLContext
*/
privatestaticSSLContextgetSSLContext(Contextcontext){
try{
//服务器端需要验证的客户端证书
KeyStorekeyStore=KeyStore.getInstance(KEY_STORE_TYPE_P12);
//客户端信任的服务器端证书
KeyStoretrustStore=KeyStore.getInstance(KEY_STORE_TYPE_BKS);
InputStreamksIn=context.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);
InputStreamtsIn=context.getResources().getAssets().open(KEY_STORE_TRUST_PATH);
try{
keyStore.load(ksIn,KEY_STORE_PASSWORD.toCharArray());
trustStore.load(tsIn,KEY_STORE_TRUST_PASSWORD.toCharArray());
}catch(Exceptione){
e.printStackTrace();
}finally{
try{
ksIn.close();
}catch(Exceptionignore){
}
try{
tsIn.close();
}catch(Exceptionignore){
}
}
SSLContextsslContext=SSLContext.getInstance("TLS");
TrustManagerFactorytrustManagerFactory=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
KeyManagerFactorykeyManagerFactory=KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore,KEY_STORE_PASSWORD.toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(),null);
returnsslContext;
}catch(Exceptione){
Log.e("tag",e.getMessage(),e);
}
returnnull;
}
/**
* 获取SSL认证需要的HttpClient
*
* @param context 上下文
* @return OkHttpClient
*/
publicstaticOkHttpClientgetSSLContextHttp(Contextcontext){
OkHttpClientclient=newOkHttpClient();
SSLContextsslContext=getSSLContext(context);
if(sslContext!=null){
client.setSslSocketFactory(sslContext.getSocketFactory());
}
returnclient;
}
/**
* 获取HttpsURLConnection
*
* @param context 上下文
* @param url 连接url
* @param method 请求方式
* @return HttpsURLConnection
*/
publicstaticHttpsURLConnectiongetHttpsURLConnection(Contextcontext,Stringurl,Stringmethod){
URLu;
HttpsURLConnectionconnection=null;
try{
SSLContextsslContext=getSSLContext(context);
if(sslContext!=null){
u=newURL(url);
connection=(HttpsURLConnection)u.openConnection();
connection.setRequestMethod(method);//"POST""GET"
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type","binary/octet-stream");
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setConnectTimeout(30000);
}
}catch(Exceptione){
e.printStackTrace();
}
returnconnection;
}
这里还没有给出Webview进行HTTPS请求的方式,正在研究中,后续有了再来更新,这里需要注意一下的就是SSLContext的默认端口是443端口,这点需要注意一下。