生成证书和秘钥
使用JDK7的keytool工具生成
转到JDK的安装目录下的bin,如/usr/java/jdk1.7.0_79/bin
生成非对称密钥
keytool -genkeypair -v -alias aaa -keyalg RSA -keystore /home/app/key/pay.jks -validity 3650 -dname "CN=pay.xxx.com,OU=,O=aaa,L=HEFEI,ST=ANHUI,C=CN" -storepass 222222 -keypass 222222
- alias 密钥仓库别名,不区分大小写
- keyalg 设置采用的算法
- keystore 秘钥文件生成位置
- validity 设置秘钥有效天数
- dname 设置证书信息
- CN 名字与姓氏,网站输入网站域名
- OU 组织单位名称
- O 组织名称
- L 城市或区域
- ST 州或省份名称
- C 单位的两字母国家代码
- storepass 仓库的密码,建议修改
- keypass key的密码,建议修改
导出证书
keytool -export -alias aaa -keystore /home/app/key/pay.jks -file /home/app/key/pay.cer
Tomcat
生成P12格式私钥
keytool -importkeystore -srckeystore /home/app/key/pay.jks -destkeystore pay.p12 -deststoretype PKCS12
使用OpenSSL生成pem格式秘钥
openssl pkcs12 -in /home/app/key/pay.p12 -out /home/app/key/pay.pem -nodes
Tomcat配置
在server.xml中添加443端口的Connector
要求Tomcat必须开启了APR支持
- SSLCertificateFile:配置为导出的证书
- SSLCertificateKeyFile:配置为OpenSSL生成的pem秘钥
<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
SSLCertificateFile="E:/key/pay.cer"
SSLCertificateKeyFile="E:/key/pay.pem"
ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_
CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_256_C
BC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RS
A_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA"/>
Nginx
生成P12格式私钥
keytool -importkeystore -srckeystore /home/app/key/pay.jks -destkeystore pay.p12 -deststoretype PKCS12
生成证书
openssl pkcs12 -in pay.p12 -nokeys -clcerts -out server-ssl.crt
openssl pkcs12 -in pay.p12 -nokeys -cacerts -out gs_intermediate_ca.crt
server-ssl.crt是SSL证书,gs_intermediate_ca.crt是中级证书,俩个合并到一起才是nginx服务器所需要的证书
合并证书
cat server-ssl.crt gs_intermediate_ca.crt >nginx.crt
生成Nginx私钥
openssl pkcs12 -nocerts -nodes -in pay.p12 -out nginx.key
Nginx配置
修改nginx的nginx.conf配置文件
server {
listen 88;
server_name 127.0.0.1;
ssl on;
ssl_certificate E:/key/nginx.crt;
ssl_certificate_key E:/key/nginx.key;
- ssl_certificate:上文生成的证书文件
- ssl_certificate_key:上文生成的Nginx私钥
开发使用
由于证书未经过权威CA验证,因此需要进行特殊配置,需要自定义只生成证书的验证方法
证书验证类
/**
* SSL客户端工具,信任设置的证书
*/
public class SSLClientUtil {
private final static Logger logger = LoggerFactory.getLogger(SSLClientUtil.class);
/**
* 设置SSL验证证书和服务器域名(地址)
* crtFiles crt证书文件,crt证书文件地址,可以通过firefox浏览器导出网站的crt文件
*/
public void setSSLVerify(File... crtFiles) {
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
try {
String peerHost = session.getPeerHost(); //服务器返回的主机名
X509Certificate[] peerCertificates = (X509Certificate[]) session.getPeerCertificates();
for (X509Certificate certificate : peerCertificates) {
X500Principal subjectX500Principal = certificate.getSubjectX500Principal();
String name = subjectX500Principal.getName();
String[] split = name.split(",");
for (String str : split) {
if (str.startsWith("CN")) {//证书绑定的域名或者ip
// CN:www.baidu.com中取得后面的主机名
String hostName = str.substring(3);
// 秘钥的主机名为请求地址才放行
if (hostName.equals(peerHost)) {
return true;
}
}
}
}
} catch (SSLPeerUnverifiedException e1) {
}
return false;
}
};
try {
trustHttpsCertificates(crtFiles);
} catch (Exception e) {
logger.error("信任指定HTTPS证书失败,可能造成Hessian Https请求失败", e);
}
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
/**
* 设置SSL验证证书,不验证服务器域名(地址)
* crtFiles crt证书文件,crt证书文件地址,可以通过firefox浏览器导出网站的crt文件
*/
public void setSSLVerifyWithoutDomain(File... crtFiles) {
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
try {
String peerHost = session.getPeerHost(); //服务器返回的主机名
X509Certificate[] peerCertificates = (X509Certificate[]) session.getPeerCertificates();
for (X509Certificate certificate : peerCertificates) {
return true;
}
} catch (SSLPeerUnverifiedException e1) {
}
return false;
}
};
try {
trustHttpsCertificates(crtFiles);
} catch (Exception e) {
logger.error("信任指定HTTPS证书失败,可能造成Hessian Https请求失败", e);
}
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
/**
* 设置可信任的crt证书
*
* @param crtFiles crt证书文件,crt证书文件地址,可以通过firefox浏览器导出网站的crt文件
* @throws Exception
*/
private void trustHttpsCertificates(File... crtFiles) throws Exception {
javax.net.ssl.TrustManager[] trustCerts = new javax.net.ssl.TrustManager[crtFiles.length];
for (int i = 0; i < crtFiles.length; i++) {
javax.net.ssl.TrustManager tm = new TrustEverythingManager(crtFiles[i]);
trustCerts[i] = tm;
}
javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
.getInstance("SSL");
sc.init(null, trustCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc
.getSocketFactory());
}
class TrustEverythingManager implements javax.net.ssl.TrustManager,
javax.net.ssl.X509TrustManager {
/**
* crt证书文件地址,可以通过firefox浏览器导出网站的该文件
*/
private File crtFile;
public TrustEverythingManager(File crtFile) {
this.crtFile = crtFile;
}
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(
java.security.cert.X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(
java.security.cert.X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
if (certs == null) {
throw new IllegalArgumentException("X509Certificate is null");
}
if (certs.length == 0) {
throw new IllegalArgumentException("X509Certificate is empty");
}
for (X509Certificate cert : certs) {
cert.checkValidity();
try {
cert.verify(getPublicKey());
} catch (Exception ex) {
logger.error("验证证书失败,可能造成Hessian Https请求失败", ex);
}
}
return;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
return;
}
public PublicKey getPublicKey() throws FileNotFoundException, CertificateException {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(crtFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(fileInputStream);
return certificate.getPublicKey();
} finally {
IOUtils.closeQuietly(fileInputStream);
}
}
}
}
使用HttpsURLConnection访问Https服务时
在访问HTTPS或项目启动时调用SSLClientUtil验证方法
仅验证证书
File file = new File("E:\\key\\pay.cer");
new SSLClientUtil().setSSLVerifyWithoutDomain(file);
验证证书和域名
File file = new File("E:\\key\\pay.cer");
new SSLClientUtil().setSSLVerify(file);
证书文件可通过Firefox浏览器导出证书,或者使用keytool -export导出的证书