加密之SSL和单双向认证
- 1 SSL
- 1.1 SSL了解
- 1.2 导入证书
- 1.3 tomcat配置SSL
- 1.4 具体操作
- 2 单向认证
- 2.1 第三方签名
- 3 双向认证
- 3.1 引言
- 3.2 使用openssl脚本
1 SSL
1.1 SSL了解
SSL
配置是我们在实际应用中经常见到的场景
SSL
(Secure Sockets Layer
,安全套接层)是为通信提供安全及数据完整性的一种安全协议,SSL
在网络传送层对网络连接进行加密。SSL
协议位于TCP/IP
协议与各种应用层协议之间,为数据通信提供安全支持。
SSL
协议可分为两层:
SSL
记录协议(SSL Record Rrotocol
),它建立在可靠的传输协议(如TCP
)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持SSL
握手协议(SSL Handshake Protocol
),它建立在SSL
记录协议之上,用于在实际数据传输开始前,通信双方进行身份认证、协商加密算法、交互加密密钥等
1.2 导入证书
我们需要构建一个由CA
机构签发的有效证书,这里我们先生成的自签名证书zlex.cer
(具体生产证书密钥步骤点此了解)
这里,我们将证书导入到我们的密钥库。
keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore
其中:
import
:表示导入alias
:指定别名,这里是www.zlex.orgfile
:指输入文件名,这里是d:/zlex.cerkeystore
:指定存储位置,这里是d:/zlex.keystore
在这里我使用的密码为654321
控制台输出:
输入keystore密码:
再次输入新密码:
所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
签发人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
序列号:4a1e48df
有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009
证书指纹:
MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A
SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4
签名算法名称:SHA1withRSA
版本: 3
信任这个认证? [否]: y
认证已添加至keystore中
1.3 tomcat配置SSL
接下来我们将域名www.zlex.org
定位到本机上。打开C:\Windows\System32\drivers\etc\hosts
文件,将www.zlex.org
绑定在本机上。在文件末尾追加127.0.0.1 www.zlex.org
。
现在通过地址栏访问http://www.zlex.org
,或者通过ping
命令,如果能够定位到本机,域名映射就搞定了。
现在,配置tomcat
。先将zlex.keystore
拷贝到tomcat
的conf
目录下,然后配置server.xml
。将如下内容加入配置文件
注意clientAuth="false"
测试阶段,置为false
,正式使用时建议使用true
。现在启动tomcat,就可以访问https://www.zlex.org/进行测试
显然,证书未能通过认证,这个时候你可以选择安装证书(上文中的zlex.cer
文件就是证书),作为受信任的根证书颁发机构导入,再次重启浏览器,访问https://www.zlex.org/,就会看到地址栏中会有个小锁
,就说明安装成功。所有的浏览器联网操作已经在RSA加密解密系统的保护之下了。但似乎我们感受不到。
这个时候很多人开始怀疑,如果我们要手工做一个这样的https的访问是不是需要把浏览器的这些个功能都实现呢?不需要!
点击了解Spring Boot配置SSL过程
1.4 具体操作
点击此处了解 获取公私钥,加密解密,加签验签,验证证书有效期
configSSLSocketFactory
方法供外界调用,该方法为HttpsURLConnection
配置了SSLSocketFactory
。当HttpsURLConnection
配置了SSLSocketFactory
后,就可以通过HttpsURLConnection
的getInputStream
、getOutputStream
,像往常使用HttpURLConnection
做操作了。尤其要说明一点,未配置SSLSocketFactory
前,HttpsURLConnection
的getContentLength()
获得值永远都是-1
/**
* 获得SSL SocektFactory
*/
private static SSLSocketFactory getSSLSocketFactory(String password,String keyStorePath, String trustKeyStorePath) throws Exception {
// 初始化密钥库
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(SunX509);
FileInputStream is = new FileInputStream(keyStorePath);
KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
keyStore.load(is, password.toCharArray());
is.close();
keyManagerFactory.init(keyStore, password.toCharArray());
// 初始化信任库
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(SunX509);
FileInputStream is_trustkeyStore = new FileInputStream(trustKeyStorePath);
KeyStore trustkeyStore = KeyStore.getInstance(KEY_STORE);
trustkeyStore.load(is_trustkeyStore, password.toCharArray());
is.close();
trustManagerFactory.init(trustkeyStore);
// 初始化SSL上下文
SSLContext ctx = SSLContext.getInstance(SSL);
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
SSLSocketFactory sf = ctx.getSocketFactory();
return sf;
}
/**
* 为HttpsURLConnection配置SSLSocketFactory
*/
public static void configSSLSocketFactory(HttpsURLConnection conn, String password, String keyStorePath, String trustKeyStorePath)throws Exception {
conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,trustKeyStorePath));
}
验证结果
private static String clientKeyStorePath = "d:/zlex-client.keystore";
private static String clientPassword = "654321";
public static void main(String[] args) throws Exception {
URL url = new URL("https://www.zlex.org/examples/");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
configSSLSocketFactory(conn, clientPassword, clientKeyStorePath, clientKeyStorePath);
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
DataInputStream dis = new DataInputStream(is);
byte[] data = new byte[length];
dis.readFully(data);
dis.close();
System.err.println(new String(data));
conn.disconnect();
}
2 单向认证
2.1 第三方签名
在上面我们使用自签名证书完成了认证。接下来,我们使用第三方CA
签名机构完成证书签名。
这里我们使用thawte提供的测试用21天免费ca证书。
- 要在该网站上注明你的域名,这里使用www.test.org作为测试用域名
- 如果域名有效,你会收到邮件要求你访问https://www.thawte.com/cgi/server/try.exe获得ca证书。
- 复述密钥库的创建。
keytool -genkey -validity 36000 -alias www.test.org -keyalg RSA -keystore d:\zlex.keystore
在这里使用的密码为 123456
控制台输出:
密码:
再次输入新密码:
您的名字与姓氏是什么?
[Unknown]: www.zlex.org
您的组织单位名称是什么?
[Unknown]: zlex
您的组织名称是什么?
[Unknown]: zlex
您所在的城市或区域名称是什么?
[Unknown]: BJ
您所在的州或省份名称是什么?
[Unknown]: BJ
该单位的两字母国家代码是什么
[Unknown]: CN
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
[否]: Y
输入的主密码
(如果和 keystore 密码相同,按回车):
再次输入新密码:
- 通过如下命令,从
zlex.keystore
中导出CA证书申请。
keytool -certreq -alias www.test.org -file d:\zlex.csr -keystore d:\zlex.keystore -v
就会获得zlex.csr
文件,可以用记事本打开,内容如下格式:
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMQswCQYDVQQHEwJCSjENMAsG
A1UEChMEemxleDENMAsGA1UECxMEemxleDEVMBMGA1UEAxMMd3d3LnpsZXgub3JnMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCR6DXU9Mp+mCKO7cv9JPsj0n1Ec/GpM09qvhpgX3FNad/ZWSDc
vU77YXZSoF9hQp3w1LC+eeKgd2MlVpXTvbVwBNVd2HiQPp37ic6BUUjSaX8LHtCl7l0BIEye9qQ2
j8G0kak7e8ZA0s7nb3Ymq/K8BV7v0MQIdhIc1bifK9ZDewIDAQABoAAwDQYJKoZIhvcNAQEFBQAD
gYEAMA1r2fbZPtNx37U9TRwadCH2TZZecwKJS/hskNm6ryPKIAp9APWwAyj8WJHRBz5SpZM4zmYO
oMCI8BcnY2A4JP+R7/SwXTdH/xcg7NVghd9A2SCgqMpF7KMfc5dE3iygdiPu+UhY200Dvpjx8gmJ
1UbH3+nqMUyCrZgURFslOUY=
-----END NEW CERTIFICATE REQUEST-----
- 将上述文件内容拷贝到
https://www.thawte.com/cgi/server/try.exe
中,点击next,获得回应内容,这里是p7b格式。
内容如下:
-----BEGIN PKCS7-----
MIIF3AYJKoZIhvcNAQcCoIIFzTCCBckCAQExADALBgkqhkiG9w0BBwGgggWxMIID
EDCCAnmgAwIBAgIQA/mx/pKoaB+KGX2hveFU9zANBgkqhkiG9w0BAQUFADCBhzEL
MAkGA1UEBhMCWkExIjAgBgNVBAgTGUZPUiBURVNUSU5HIFBVUlBPU0VTIE9OTFkx
HTAbBgNVBAoTFFRoYXd0ZSBDZXJ0aWZpY2F0aW9uMRcwFQYDVQQLEw5URVNUIFRF
U1QgVEVTVDEcMBoGA1UEAxMTVGhhd3RlIFRlc3QgQ0EgUm9vdDAeFw0wOTA1Mjgw
MDIxMzlaFw0wOTA2MTgwMDIxMzlaMFwxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJC
SjELMAkGA1UEBxMCQkoxDTALBgNVBAoTBHpsZXgxDTALBgNVBAsTBHpsZXgxFTAT
BgNVBAMTDHd3dy56bGV4Lm9yZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
keg11PTKfpgiju3L/ST7I9J9RHPxqTNPar4aYF9xTWnf2Vkg3L1O+2F2UqBfYUKd
8NSwvnnioHdjJVaV0721cATVXdh4kD6d+4nOgVFI0ml/Cx7Qpe5dASBMnvakNo/B
tJGpO3vGQNLO5292JqvyvAVe79DECHYSHNW4nyvWQ3sCAwEAAaOBpjCBozAMBgNV
HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBABgNVHR8E
OTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNl
cnZlckNBLmNybDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v
Y3NwLnRoYXd0ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEATPuxZbtJJSPmXvfrr1yz
xqM06IwTZ6UU0lZRG7I0WufMjNMKdpn8hklUhE17mxAhGSpewLVVeLR7uzBLFkuC
X7wMXxhoYdJZtNai72izU6Rd1oknao7diahvRxPK4IuQ7y2oZ511/4T4vgY6iRAj
q4q76HhPJrVRL/sduaiu+gYwggKZMIICAqADAgECAgEAMA0GCSqGSIb3DQEBBAUA
MIGHMQswCQYDVQQGEwJaQTEiMCAGA1UECBMZRk9SIFRFU1RJTkcgUFVSUE9TRVMg
T05MWTEdMBsGA1UEChMUVGhhd3RlIENlcnRpZmljYXRpb24xFzAVBgNVBAsTDlRF
U1QgVEVTVCBURVNUMRwwGgYDVQQDExNUaGF3dGUgVGVzdCBDQSBSb290MB4XDTk2
MDgwMTAwMDAwMFoXDTIwMTIzMTIxNTk1OVowgYcxCzAJBgNVBAYTAlpBMSIwIAYD
VQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRUaGF3dGUg
Q2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAaBgNVBAMT
E1RoYXd0ZSBUZXN0IENBIFJvb3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
ALV9kG+Os6x/DOhm+tKUQfzVMWGhE95sFmEtkMMTX2Zi4n6i6BvzoReJ5njzt1LF
cqu4EUk9Ji20egKKfmqRzmQFLP7+1niSdfJEUE7cKY40QoI99270PTrLjJeaMcCl
+AYl+kD+RL5BtuKKU3PurYcsCsre6aTvjMcqpTJOGeSPAgMBAAGjEzARMA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAgozj7BkD9O8si2V0v+EZ/t7E
fz/LC8y6mD7IBUziHy5/53ymGAGLtyhXHvX+UIE6UWbHro3IqVkrmY5uC93Z2Wew
A/6edK3KFUcUikrLeewM7gmqsiASEKx2mKRKlu12jXyNS5tXrPWRDvUKtFC1uL9a
12rFAQS2BkIk7aU+ghYxAA==
-----END PKCS7-----
将其存储为zlex.p7b
6. 将由CA签发的证书导入密钥库。
keytool -import -trustcacerts -alias www.test.org -file d:\zlex.p7b -keystore d:\zlex.keystore -v
在这里使用的密码为 123456
控制台输出:
输入keystore密码:
回复中的最高级认证:
所有者:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
TESTING PURPOSES ONLY, C=ZA
签发人:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
TESTING PURPOSES ONLY, C=ZA
序列号:0
有效期: Thu Aug 01 08:00:00 CST 1996 至Fri Jan 01 05:59:59 CST 2021
证书指纹:
MD5:5E:E0:0E:1D:17:B7:CA:A5:7D:36:D6:02:DF:4D:26:A4
SHA1:39:C6:9D:27:AF:DC:EB:47:D6:33:36:6A:B2:05:F1:47:A9:B4:DA:EA
签名算法名称:MD5withRSA
版本: 3
扩展:
#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
... 是不可信的。 还是要安装回复? [否]: Y
认证回复已安装在 keystore中
[正在存储 d:\zlex.keystore]
- 域名定位
将域名www.test.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.test.org绑定在本机上。在文件末尾追加127.0.0.1 www.test.org
。现在通过地址栏访问http://www.test.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了 - 配置server.xml
3 双向认证
3.1 引言
对于双向认证,做一个简单的描述。
服务器端下发证书,客户端接受证书。证书
带有公钥
信息,用于验证服务器端、对数据加密/解密,起到OSI五类服务的认证(鉴别)服务和保密性服务。 这只是单向认证,为什么?
因为客户端可以验证服务器端,但服务器端不能验证客户端!
如果客户端也有这样一个证书,服务器端也就能够验证客户端,这就是双向认证了,换言之,当你用银行的U盾
之类的U盘与银行账户交互时,在你验证银行服务器的同时,服务器也在验证你!这种双重验证,正是网银系统的安全关键!
双向认证需要一个CA
机构签发这样的客户端、服务器端证书,首先需要CA
机构构建一个根证书。keytool
可以构建证书但不能构建我们需要的根证书,openssl
则可以,根证书签发客户端证书,根私钥签发服务器端证书
直接使用linux下的openssl
来完成CA
,需要修改openssl.cnf
文件,在ubuntu下的/etc/ssl/目录下,找到[ CA_default ]修改dir变量。
#dir = ./demoCA # Where everything is kept
我们把c盘的ca目录作为CA认证的根目录,文件修改后如下所示:
dir = $ENV::HOME/ca # Where everything is kept
3.2 使用openssl脚本
我们需要在用户目录下构建一个ca目录,以及子目录,如下所下:
ca
|__certs
|__newcerts
|__private
|__crl
执行如下操作:
#!/bin/bash
ca_path=ca
certs_path=$ca_path/certs
newcerts_path=$ca_path/newcerts
private_path=$ca_path/private
crl_path=$ca_path/crl
echo 移除CA根目录
rm -rf ca
echo 构建CA根目录
mkdir ca
echo 构建子目录
mkdir certs
mkdir newcerts
mkdir private
mkdir crl
#构建文件
touch $ca_path/index.txt
echo 01 > $ca_path/serial
echo
#构建随机数
openssl rand -out $private_path/.rand 1000
echo
echo 生成根证书私钥
openssl genrsa -des3 -out $private_path/ca.pem 2048
echo
echo 查看私钥信息
openssl rsa -noout -text -in $private_path/ca.pem
echo
echo 生成根证书请求
openssl req -new -key $private_path/ca.pem -out $certs_path/ca.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=ca.zlex.org"
echo
echo 查看证书请求
openssl req -in $certs_path/ca.csr -text -noout
echo
echo 签发根证书
openssl ca -create_serial -out $certs_path/ca.crt -days 3650 -batch -keyfile $private_path/ca.pem -selfsign -extensions v3_ca -infiles $certs_path/ca.csr
#openssl x509 -req -sha1 -extensions v3_ca -signkey $private_path/ca.pem -in $certs_path/ca.csr -out $certs_path/ca.crt -days 3650
echo
echo 查看证书详情
openssl x509 -in $certs_path/ca.crt -text -noout
echo
echo 证书转换——根证书
openssl pkcs12 -export -clcerts -in $certs_path/ca.crt -inkey $private_path/ca.pem -out $certs_path/ca.p12
echo
echo 生成服务器端私钥
openssl genrsa -des3 -out $private_path/server.pem 1024
echo
echo 查看私钥信息
openssl rsa -noout -text -in $private_path/server.pem
echo
echo 生成服务器端证书请求
openssl req -new -key $private_path/server.pem -out $certs_path/server.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=www.zlex.org"
echo
echo 查看证书请求
openssl req -in $certs_path/server.csr -text -noout
echo
echo 签发服务器端证书
openssl ca -in $certs_path/server.csr -out $certs_path/server.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem -days 365 -notext
#openssl x509 -req -days 365 -sha1 -extensions v3_req -CA $certs_path/ca.crt -CAkey $private_path/ca.pem -CAserial $ca_path/serial -CAcreateserial -in $certs_path/server.csr -out $certs_path/server.crt
echo
echo 查看证书详情
openssl x509 -in $certs_path/server.crt -text -noout
echo
echo 证书转换——服务器端
openssl pkcs12 -export -clcerts -in $certs_path/server.crt -inkey $private_path/server.pem -out $certs_path/server.p12
echo
echo 生成客户端私钥
openssl genrsa -des3 -out $private_path/client.pem 1024
echo
echo 生成客户端私钥
openssl genrsa -des3 -out $private_path/client.pem 1024
echo
echo 查看私钥信息
openssl rsa -noout -text -in $private_path/client.pem
echo
echo 生成客户端证书请求
openssl req -new -key $private_path/client.pem -out $certs_path/client.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=zlex"
echo
echo 查看证书请求
openssl req -in $certs_path/client.csr -text -noout
echo
echo 签发客户端证书
openssl ca -in $certs_path/client.csr -out $certs_path/client.crt -cert $certs_path/ca.crt -keyfile $private_path/ca.pem -days 365 -notext
#openssl x509 -req -days 365 -sha1 -extensions dir_sect -CA $certs_path/ca.crt -CAkey $private_path/ca.pem -CAserial $ca_path/serial -in $certs_path/client.csr -out $certs_path/client.crt
echo
echo 查看证书详情
openssl x509 -in $certs_path/client.crt -text -noout
echo
echo 证书转换——客户端
openssl pkcs12 -export -clcerts -in $certs_path/client.crt -inkey $private_path/client.pem -out $certs_path/client.p12
echo
echo 生成证书链PKCS#7
openssl crl2pkcs7 -nocrl -certfile $certs_path/server.crt -certfile $certs_path/ca.crt -certfile $certs_path/client.crt -out
form PEM -out $certs_path/zlex.p7b
echo
echo 查看证书链
openssl pkcs7 -in $certs_path/zlex.p7b -print_certs -noout