目录
1.为什么要做https和wss升级
2.https的相关名称解释
3.证书格式
4.oepnssl使用 安装OpenSSL
生成证书
解决subjectAlternativeName 找不到的问题
客户端
服务器端
5.xca使用
xca简介
xca安装步骤
xca使用
Step1. 打开XCA
Step2. 如果是第一次打开,请点击 File-->New Database
Step3. 选择证书数据存储的位置并命名存储文件的名字。
Step4. 设置密码保护证书数据
Step5: 鼠标点击到“Certificate Signing Request” 分页,并点击右边的“New Request”,将弹出下面的窗体。
Step6. 生成保护证书的秘钥:因为我们是生成证书请求,那么我们需要生成一个证书对,即私钥和公钥,上面Step6生成的信息是在公钥中可以看到的。
6.单向认证和双向认证
7.免费证书申请
8.单tomcat下配置https和wss
方案1
方案2
9.nginx+tomcat下配置半链路https和wss
部署说明
nginx 配置
10.nginx+tomcat下配置全链路https和wss
11.uat 环境如何使用免费证书
12.代码优化项
13.参考资料
直接原因:chrome62版本以后不支持http网站通知,但是网站通知又是一个非常重要的功能,用在新用户接入提醒,消息提醒,差评提醒,系统提醒等。
根本原因:Chrome和Firefox明确标记在没有HTTPS的情况下网站是不安全的,增加https后,提升了安全性。
窃听风险(eavesdropping):第三方可以获知通信内容。
篡改风险(tampering):第三方可以修改通信内容。
冒充风险(pretending):第三方可以冒充他人身份参与通信。
所有信息都是加密传播,第三方无法窃听。
具有校验机制,一旦被篡改,通信双方会立刻发现。
配备身份证书,防止身份被冒充。
TLS与SSL对于不是专业搞安全的开发人员来讲,可以认为是差不多的,这二者是并列关系,详细差异见 http://kb.cnblogs.com/page/197396/
PEM - Privacy Enhanced Mail,打开看文本格式,以"-----BEGIN..."开头, "-----END..."结尾,内容是BASE64编码.
Apache和*NIX服务器偏向于使用这种编码格式.
DER - Distinguished Encoding Rules,打开看是二进制格式,不可读.
Java和Windows服务器偏向于使用这种编码格式
一般来说,主流的Web服务软件,通常都基于两种基础密码库:OpenSSL和Java。
Tomcat、Weblogic、JBoss等,使用Java提供的密码库。通过Java的Keytool工具,生成Java Keystore(JKS)格式的证书文件。
Apache、Nginx等,使用OpenSSL提供的密码库,生成PEM、KEY、CRT等格式的证书文件。
此外,IBM的产品,如Websphere、IBM Http Server(IHS)等,使用IBM产品自带的iKeyman工具,生成KDB格式的证书文件。微软Windows Server中的Internet Information Services(IIS),使用Windows自带的证书库生成PFX格式的证书文件。
如果您在工作中遇到带有后缀扩展名的证书文件,可以简单用如下方法区分:
*.DER *.CER : 这样的证书文件是二进制格式,只含有证书信息,不包含私钥。
*.CRT : 这样的文件可以是二进制格式,也可以是文本格式,一般均为文本格式,功能与*.DER/*.CER相同。
*.PEM : 一般是文本格式,可以放证书或私钥,或者两者都包含。 *.PEM如果只包含私钥,那一般用 *.KEY代替。
*.PFX *.P12 是二进制格式,同时含证书和私钥,一般有密码保护。
怎么判断是文本格式还是二进制?
用记事本打开,如果是规则的数字字母,如
—–BEGIN CERTIFICATE—–
MIIE5zCCA8+gAwIBAgIQN+whYc2BgzAogau0dc3PtzANBgkqh......
—–END CERTIFICATE—–
就是文本的,上面的BEGIN CERTIFICATE,说明这是一个证书
如果是—–BEGIN RSA PRIVATE KEY—–,说明这是一个私钥
这些证书格式之间是可以互相转换的
以下提供了一些证书之间的转换方法:
可以使用Keytool工具,将JKS格式转换为PFX格式。
keytool -importkeystore -srckeystore D:\server.jks -destkeystore D:\server.pfx -srcstoretype JKS -deststoretype PKCS12
可以使用Keytool工具,将PFX格式转换为JKS格式。
keytool -importkeystore -srckeystore D:\server.pfx -destkeystore D:\server.jks -srcstoretype PKCS12 -deststoretype JKS
使用OpenSSL工具,可以将密钥文件KEY和公钥文件CRT转化为PFX文件。
将密钥文件KEY和公钥文件CRT放到OpenSSL目录下,打开OpenSSL执行以下命令:
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt
使用OpenSSL工具,可以将PFX文件转化为密钥文件KEY和公钥文件CRT。
将PFX文件放到OpenSSL目录下,打开OpenSSL执行以下命令:
openssl pkcs12 -in server.pfx -nodes -out server.pem
openssl rsa -in server.pem -out server.key
请注意:此步骤是专用于使用keytool生成私钥和CSR申请证书,并且获取到pem格式证书公钥的情况下做分离私钥使用的,所以在实际部署证书时请使用此步骤分离出来的私钥和申请下来的公钥证书做匹配使用。
云盾证书服务统一使用 PEM 格式的数字证书文件。
本来准备直接放一个链接在这的,但是感觉不是特别的准确,所以借鉴这个在写详细一些。
(1)OpenSSL下载( [https://www.openssl.org/source/(https://www.openssl.org/source/))没有提供windows版本的安装包,可以选择其他开源平台提供的工具。例如 http://slproweb.com/products/Win32OpenSSL.html
根据你的系统来选择不同的版本下载安装
点击运行文件后直接下一步就可以了,没有什么特殊设置。
(2)设置环境变量,例如工具安装在C:\OpenSSL-Win64,则将C:\OpenSSL-Win64\bin;复制到Path中
这个路径需要写自己的安装路径呀,exe在bin文件中,所以记得加上bin
部分借鉴:https://blog.csdn.net/kitok/article/details/72957185
在bin(bin文件在安装目录里面)文件中打开cmd,依次输入以下命令:
注意:这里有一个大坑,我也是爬了好久的,所以先跟大家说明一下,再详细说命令。
由于Chrome 58 及以上版本只会使用 subjectAlternativeName 扩展程序(而不是 commonName)来匹配域名和网站证书。如果直接按照下面的步骤生成的ssl证书是不能直接在谷歌正常使用的,会说证书无效,并报错:NET::ERR_CERT_COMMON_NAME_INVALID 。
以下是谷歌对这个错误的说明(https://support.google.com/chrome/a/answer/7391219?hl=zh-Hans)
所以需要先解决这个问题,以下为解决步骤:
主要修改在openssl.cnf(该文件在bin文件中)
确保req下存在以下2行(默认第一行是有的,第2行被注释了)
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
确保req_distinguished_name下没有 0.xxx 的标签,有的话把0.xxx的0. 去掉
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = ShangHai
localityName = Locality Name (eg, city)
localityName_default = ShangHai
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Domain Control Validated
commonName = Internet Widgits Ltd
commonName_max = 64
新增最后一行内容 subjectAltName = @alt_names(前2行默认存在)
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
新增 alt_names,注意括号前后的空格,DNS.x 的数量可以自己加
[ alt_names ]
DNS.1 = abc.example.com
DNS.2 = dfe.example.org
DNS.3 = ex.abcexpale.net
DNS.4 = localhost
以上是为了添加‘使用者备用名称(DNS)’,在下面的命令中会用到v3_req 也就是这几个DNS,证书里面才会多一个‘使用者备用名称’,如图:
借鉴:http://blog.51cto.com/colinzhouyj/1566438
生成私钥(key文件):
openssl genrsa -out client.key 2048
生成csr文件:
openssl req -new -key client.key -out client.csr -config openssl.cnf
这里需要填写一些基本信息:
生成私钥(key文件):
openssl genrsa -out server.key 2048
生成csr文件
openssl req -new -key server.key -out server.csr -config openssl.cnf
这里的Common Name 写主要域名就好了(注意:这个域名也要在openssl.cnf的DNS.x里)
CSR文件必须有CA的签名才可形成证书.可将此文件发送到verisign等地方由它验证,要交一大笔钱,何不自己做CA呢.
1、在bin目录下新建目录 demoCA、demoCA/certs、demoCA/certs、demoCA/newcerts
2、在demoCA建立一个空文件 index.txt
3、在demoCA建立一个文本文件 serial, 没有扩展名,内容是一个合法的16进制数字,例如 0000
4、生成ca.key并自签署
openssl req -new -x509 -days 3650 -keyout ca.key -out ca.crt -config openssl.cnf
用生成的CA的证书为刚才生成的server.csr,client.csr文件签名:
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -extensions v3_req -config openssl.cnf
openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile ca.key -extensions v3_req -config openssl.cnf
到这里证书就生成好了
参考https://blog.csdn.net/chancein007/article/details/27239605
一般情况下,大家能想到管理和生成SSL证书的方法就是OpenSSL程序或者用JDK自带的keytool命令,但是这两种工具虽然功能强大,但是用户的可操作性并不好,需要用户记住并输入一些命令,而且也不是特别的直观。那么有没有一个好的证书管理工具,能帮我们方便的管理证书,方便的生成证书请求,方便的对证书进行自签名,方便的把证书以一种格式导入进来,然后以自己想要的另外一种格式导出去呢?答案就是,XCA(X Certificate and key management),XCA是一个开源的工具,底层还是基于openSSL的类库和API的。
Step1 . 打开浏览器,输入下载地址
Step2 . 然后点击“direct link” 或者 "mirror"标签,下载最新的版本,最新的版本是:1.3.2
Step3. 安装下载后的exe文件
Step4. 这样在你的开始菜单中,就能找到xca的目录,打开xca的程序即可。
好了,使用XCA生成SSL证书请求的介绍就完成了,如果你还有其他的问题,请给我在文章后面留言,我将会尽可能在第一时间回复。
单向认证:只需要验证SSL服务器身份,不需要验证SSL客户端身份。
双向认证:要求服务器和客户端双方都有证书,客户端需要校验服务端,服务端也需要校验客户端。
SSL双向认证和SSL单向认证的区别
双向认证 SSL 协议要求服务器和用户双方都有证书。单向认证 SSL 协议不需要客户拥有CA证书,具体的过程相对于上面的步骤,只需将服务器端验证客户证书的过程去掉,以及在协商对称密码方案,对称通话密钥时,服务器发送给客户的是没有加过密的(这并不影响 SSL 过程的安全性)密码方案。这样,双方具体的通讯内容,就是加过密的数据,如果有第三方攻击,获得的只是加密的数据,第三方要获得有用的信息,就需要对加密的数据进行解密,这时候的安全就依赖于密码方案的安全。而幸运的是,目前所用的密码方案,只要通讯密钥长度足够的长,就足够的安全。这也是我们强调要求使用128位加密通讯的原因。
一般Web应用都是采用SSL单向认证的,原因很简单,用户数目广泛,且无需在通讯层对用户身份进行验证,一般都在应用逻辑层来保证用户的合法登入。但如果是企业应用对接,情况就不一样,可能会要求对客户端(相对而言)做身份验证。这时就需要做SSL双向认证。
修改tomcat配置
public EmbeddedServletContainerFactory createEmbeddedServletContainerFactory() throws IOException, NoSuchAlgorithmException
{
TomcatEmbeddedServletContainerFactory tomcatFactory = new TomcatEmbeddedServletContainerFactory();
tomcatFactory.addConnectorCustomizers(new UKeFuTomcatConnectorCustomizer(maxthread, maxconnections));
File sslFile = new File(path , "ssl/https.properties") ;
if(sslFile.exists()){
Properties sslProperties = new Properties();
FileInputStream in = new FileInputStream(sslFile);
sslProperties.load(in);
in.close();
if(!StringUtils.isBlank(sslProperties.getProperty("key-store")) && !StringUtils.isBlank(sslProperties.getProperty("key-store-password"))){
Ssl ssl = new Ssl();
ssl.setKeyStore(new File(path , "ssl/"+sslProperties.getProperty("key-store")).getAbsolutePath());
ssl.setKeyStorePassword(UKTools.decryption(sslProperties.getProperty("key-store-password")));
tomcatFactory.setSsl(ssl);
}
}
return tomcatFactory;
}
chrome和nginx之间使用https和wss,nginx和tomcat之间依然使用http和ws
upstream x.com.cn {
session_sticky;
server 10.115.88.2:3230 max_fails=3 fail_timeout=10s;
server 10.115.88.3:3231 max_fails=3 fail_timeout=10s;
server 10.115.88.4:3230 max_fails=3 fail_timeout=10s;
}
upstream x.com.cn.ws {
server 10.115.88.2:9081 max_fails=3 fail_timeout=10s;
server 10.115.88.3:9081 max_fails=3 fail_timeout=10s;
server 10.115.88.4:9081 max_fails=3 fail_timeout=10s;
}
server {
listen 80;
server_name you.com;
listen 443 ssl http2 spdy;
ssi on;
ssi_silent_errors on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_redirect http:// $scheme://;
proxy_set_header X-Forwarded-Proto $scheme;
ssl_certificate /server.crt;
ssl_certificate_key /server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets on;
ssl_session_ticket_key /app/tengine/uat/key/tls_session_ticket.key;
keepalive_timeout 75s;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
resolver 114.114.114.114;
ssl_dhparam /dhparam.pem;
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://x.com.cn;
}
}
server {
server_name you.com;
listen 443 ssl http2 spdy;
ssi on;
ssi_silent_errors on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_redirect http:// $scheme://;
proxy_set_header X-Forwarded-Proto $scheme;
ssl_certificate /server.crt;
ssl_certificate_key /server.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets on;
ssl_session_ticket_key /app/tengine/uat/key/tls_session_ticket.key;
keepalive_timeout 75s;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
resolver 114.114.114.114;
ssl_dhparam /dhparam.pem;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_pass http://x.com.cn.ws;
}
}
uat环境更换域名为x.com.cn,使用正式证书,在nginx上配置,然后本地配置host,进行测试
socketio请求,使用js进行http或者https适配
图片请求,修改为//,使可以自动适配,包含访客头像,消息里的图片
表情处理,表情显示在聊天窗口也是以图片显示的
代码中使用request.getScheme()、request.getRequestURL() 会获取到的是 HTTP
Nginx 域名 HTTP 请求 302 跳转实现全站 HTTPS
iframe里的redirect处理,需要强制返回https
/**
* 重写sendRedirect方法。http强制为https
*
*/
public class HttpsServletResponseWrapper extends HttpServletResponseWrapper {
private final HttpServletRequest request;
public HttpsServletResponseWrapper(HttpServletRequest request2,HttpServletResponse response) {
super(response);
this.request=request2;
}
@Override
public void sendRedirect(String location) throws IOException {
if(location.startsWith("http://")||location.startsWith("https://")){
//绝对路径,直接跳转。
super.sendRedirect(location);
return;
}
//假设前端请求头为http_https_scheme,可以传入的值有http或https,不传默认为https。
if(("http").equals(request.getHeader("http_https_scheme"))){
//http请求,默认行为。
super.sendRedirect(location);
return;
}
// 收集请求信息,为拼接绝对地址做准备。
String serverName = request.getServerName();
int port = request.getServerPort();
String contextPath = request.getContextPath();
String servletPath = request.getServletPath();
String queryString = request.getQueryString();
// 拼接绝对地址
StringBuilder absoluteUrl = new StringBuilder();
// 强制使用https
absoluteUrl.append("https").append("://").append(serverName);
//80和443位http和https默认接口,无需拼接。
if (port != 80 && port != 443) {
absoluteUrl.append(":").append(port);
}
// 将相对地址加入。
absoluteUrl.append(location);
// 跳转到绝对地址。
super.sendRedirect(absoluteUrl.toString());
}
}
http://baike.baidu.com/view/7615.htm?fr=aladdin
http://www.ibm.com/developerworks/cn/security/se-pkiusing/index.html?ca=drs
http://www.ibm.com/developerworks/cn/security/s-pki/
http://en.wikipedia.org/wiki/X.509
http://zh.wikipedia.org/wiki/PKCS
http://blog.csdn.net/rztyfx/article/details/6919220
http://weekend.blog.163.com/blog/static/7468958201131591422649/
http://linux.chinaunix.net/techdoc/beginner/2009/06/29/1120721.shtml
http://www.360doc.com/content/12/0414/19/3725126_203594495.shtml
http://blog.sina.com.cn/s/blog_436fe8b10100r5p3.html
http://blog.csdn.net/allwtg/article/details/4982507
http://rhythm-zju.blog.163.com/blog/static/310042008015115718637/
http://www.mike.org.cn/articles/ubuntu-config-apache-https/