自签名数字证书的使用

写这篇文章的起因是遇到了需要本机配置支持HTTPS协议的情况。

我们知道,因为HTTPS的安全性,越来越多的网络应用支持HTTPS,比如GOOGLE优先搜索支持HTTPS的网站;又比如小程序的网络请求只能是HTTPS的。

于是就先回顾了一下OSI模型、HTTP和HTTPS协议,感兴趣可以点击下面链接参考笔者之前的文章:
图解OSI七层模型
http协议
http协议之 8 种请求类型介绍
https协议

要支持HTTPS这边就涉及获取CA证书的问题,正式开发当然需要去购买,如果像笔者这样只是开发内测,可以用自己生成的自签名证书。生成数字证书的工具有openssl、keytool等。

openssl:SSL 密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持SSL/TLS 协议的实现。

keytool:JDK里面内置的一个数字证书生产工具,只能生成自签名的数字证书。且不支持导出私钥。所有的数字证书是以一条一条(采用别名区别)的形式存入证书库的中,证书库中的每个证书包含该条证书的私钥,公钥和对应的数字证书的信息。

一、CA的签发过程

1)服务方 S 向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证(申请证书不需要提供私钥,确保私钥永远只能服务器掌握)
2)CA 通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等
3)如信息审核通过,CA 会向申请者签发认证文件-证书。
  证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA 的信息、有效时间、证书序列号等信息的明文,同时包含一个签名
  签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA 的私钥对信息摘要进行加密,密文即签名
4)客户端 C 向服务器 S 发出请求时,S 返回证书文件
5)客户端 C 读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应 CA 的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认 证书的合法性,即公钥合法
6)客户端然后验证证书相关的域名信息、有效时间等信息
7)客户端会内置信任 CA 的证书信息(包含公钥),如果CA不被信任,则找不到对应 CA 的证书,证书也会被判定非法

在这个过程注意几点:

1)申请证书不需要提供私钥,确保私钥永远只能服务器掌握
2)证书的合法性仍然依赖于非对称加密算法,证书主要是增加了服务器信息以及签名
3)内置 CA 对应的证书称为根证书,颁发者和使用者相同,自己为自己签名(用CA自己的私钥签名),即自签名证书(此证书中的公钥即为CA的公钥,可以使用这个公钥对证书的签名进行校验,无需另外一份证书)
4)证书=公钥+申请者与颁发者信息+签名

公共钥匙用来加密数据,私有钥匙用来计算签名.
公钥加密的消息只能用私钥解密,私钥签名的消息只能用公钥检验签名。

二、https的通信过程

服务端需要认证的通信过程

  • 客户端发送请求到服务器端
  • 服务器端返回证书和公开密钥,公开密钥作为证书的一部分而存在
  • 客户端验证证书和公开密钥的有效性,如果有效,则生成共享密钥并使用公开密钥加密发送到服务器端
  • 服务器端使用私有密钥解密数据,并使用收到的共享密钥加密数据,发送到客户端
  • 客户端使用共享密钥解密数据
  • SSL加密建立………

客户端认证过程

客户端需要认证的过程跟服务器端需要认证的过程基本相同,并且少了最开始的两步。这种情况都是证书存储在客户端,并且应用场景比较少,一般金融才使用,比如支付宝、银行客户端都需要安装证书。

三、keytool的使用

keytool是JDK自带的密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以及认证服务。

密钥库中的条目类型只有两种:密钥项和可信任的证书项!

  • 密钥项- 每项存放极为敏感的加密密钥信息,这种信息以一种受保护的格式储存以防止未授权的访问。通常,储存在这类项中的密钥是机密密钥,或是伴有用于认证相应公钥用的证书“链”的私钥。keytool 只处理后一类型的项,即私钥及其关联的证书链。

  • 可信任的证书项 - 每项包含一个属于另一团体的公钥证书。它之所以叫做“可信任的证书”,是因为密钥仓库的拥有者相信证书中的公钥确实属于证书“主体”(拥有者)识别的身份。证书签发人通过对证书签名来保证这点。

创建证书

//创建方式1:交互式;这种方式会一步步提示信息,按提示输入相应信息即可
keytool -genkeypair -alias mytomcat -keyalg RSA -keysize 1024 -keypass 123456 -validity 365 -keystore d:\mykeystore.keystore -storepass 123456


//创建方式2:一步到位;把所需要的信息一步填写完整,就不用分步操作了
keytool -genkeypair -alias "mytomcat" -keyalg "RSA" -keystore "d:\mykeystore.keystore"  -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass "123456" -storepass -validity 180

这边对命令稍微解释一下,keytool 的数据保存在密钥库里,访问密钥库需要密码,访问密钥库中某个条目也是需要密码,所以在创建密钥库、密钥条目的时候,需要设定这两种密码,创建证书的过程中通过-storepass设定访问密钥库的密码,-keypass设定访问密钥库中访问某个条目的密码。

在交互式证书创建过程中,“您的名字和姓氏”填写自己应用的域名,具体域名根据自己实际需求修改,其他信息可以任意填写。还有就是-alias后面的参数值(别名)在密钥库中是不区分大小写的。

参数说明:

-genkeypair  表示要创建一个新的密钥
-dname  表示密钥的Distinguished Names,  表明了密钥的发行者身份
      CN=commonName 注:生成证书时,CN要和服务器的域名相同,如果在本地测试,就使用localhost
      OU=organizationUnit
      O=organizationName
      L=localityName
      S=stateName
      C=country
  
-keyalg    使用加密的算法,这里是RSA
-alias     和keystore关联的别名,这个alias通常不区分大小写
-keypass      私有密钥的密码,这里设置为 123456
-keystore     密钥保存在D:盘目录下的mykeystore文件中
-storepass   存取密码,这里设置为changeit,这个密码提供系统从mykeystore文件中将信息取出
-validity    该密钥的有效期为 180天 (默认为90天)

下面是各选项的缺省值。 
-alias "mykey"
-keyalg "DSA"
-keysize 1024
-validity 90
-keystore 用户宿主目录中名为 .keystore 的文件
-file 读时为标准输入,写时为标准输出 

附:cacerts证书文件是Java系统的CA证书仓库,存在于java.home\jre\lib\security目录下,这个仓库的默认访问密码是chageit
使用命令可以查看Java系统的CA证书仓库中所有证书:
keytool -list -keystore "yourPath/cacerts"

导出证书

通过上面的步骤生成了一个名为 mykeystore.keystore 的证书库,以及存储在里面的一条名为 mytomcat 的证书项;
下面导出证书:

keytool -export -alias mytomcat -keystore d:\mykeystore -file d:\mycerts.crt -storepass 123456

查看导出的证书信息
keytool -printcert -file mycerts.crt

把证书导入浏览器

在浏览器中导入这个证书,如果是购买的CA证书,就不需要导入,浏览器已内置了CA的根证书,在客户端验证证书时直接向CA请求验证;我们自己生成的证书是没有经过CA认证的,所以只能自己手动导入

四、配置tomcat,使https访问生效

1、redirectport改为443
为了使部署在tomcat中的应用可以提供https访问能力,我们需要修改%tomcat_home%/conf/server.xml文件,将下面配置的redirectport改为443:

<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"/>

修改后:

<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="443"/>

这个redirectPort的作用是当客户端以http方式访问某个服务端资源,而这个资源又要求必须https访问时,服务器重定向的端口号。这边为什么需要将8443改为443呢?是因为443是https默认的端口,就像80是http默认的端口一样,如果端口设置成443,那么在使用https访问服务器的时候不需要带端口,如果设置成8443,那么必须带端口。

2、启用SSL
将下面这行注释去掉,启用SSL,并且将端口从8443改为443,并且加上密钥库路径以及密钥库访问的密码:

<!--
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
       maxThreads="150" scheme="https" secure="true"
       clientAuth="false" sslProtocol="TLS" />
-->

增加两个参数,keystoreFile指定“密钥库”,keystorePass指定密钥库的密码。
修改后:

<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
       maxThreads="150" scheme="https" secure="true"
       clientAuth="false" sslProtocol="TLS" keystoreFile="d:/mytomcat.keystore" keystorePass="123456"/>

这样并没有完全配置好,还要注意protocol属性值

<Connector port="443" protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true"
        maxThreads="150" scheme="https" secure="true"
        clientAuth="false" sslProtocol="TLS" keystoreFile="d:/mytomcat.keystore" keystorePass="123456"/> 

3、访问http的时候自动跳转到https
现在为了能在访问http的时候自动跳转到https,还需要配置%tomcat_home%/conf/web.xml,将下面这段代码加入到节点:<welcome-file-list />后面:

 <login-config>
    <auth-method>CLIENT-CERT</auth-method>
    <realm-name>Client Cert Users-only Area</realm-name>
</login-config>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>SSL</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

如果要取消http自动跳转https,那么将这段删除或者将CONFIDENTIAL改成NONE即可!

五、使用openssl生成证书

根据上面的步骤已经可以正常部署项目,并通过https://localhost/yourdemo 访问了。

但是,使用 keytool 生成的证书在 chrome58 之前没有问题,在 chrome58 之后,会 COMMON_NAME 报错:

Error: "Subject Alternative Name Missing" 
or
NET::ERR_CERT_COMMON_NAME_INVALID 
or
"Your connection is not private"

本地证书被拒绝的原因是,Chrome已经不再支持证书中的commonName匹配,实际上,自2017年1月起就需要 subjectAltName 这个规则了。chrome58后 commonName 改成了SubjectAlternativeName 校验域名;
所以要通过对已有证书添加 v3.ext 重新生成证书,在 v3.ext 里加入 subjectAltName

解决方案:使用 OpenSSL 生成所有的证书。

  • 1、生成私钥
    openssl genrsa -des3 -out rootCA.key 2048 密码123456
    生成证书申请文件(文件中只包含了公钥和一些认证实体信息:用来做证书申请的)
    openssl req -new -key rootCA.key -out rootCA.csr
    生成根证书
    openssl x509 -req -days 365 -sha256 -extfile d:\ssl\ssl.cnf -extensions v3_ca -signkey rootCA.key -in rootCA.csr -out rootCA.crt

  • 2、根证书pem
    openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

  • 3、创建一个新的OpenSSL配置文件,server.csr.cnf

[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=CN
ST=shenzhen
L=shenzhen
O=shenzhen
OU=shenzhen
emailAddress=admin@localhost
CN=localhost
  • 4、创建一个v3.ext文件,以创建一个X509 v3证书。注意我们指定了subjectAltName选项。
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
  • 5、创建证书密钥server.key;以存储有 localhost 的配置 server.csr.cnf 进行设置 。
    openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <(cat server.csr.cnf)

  • 6、证书签名请求通过我们之前创建的根证书rootCA.pem颁发,创建出一个localhost的域名证书。输出证书文件server.crt。
    openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext

  • 7、将文件server.key和server.crt文件复制到服务器上可访问的位置(具体操作可参考本文最后的总结部分)

以上7个步骤,1、2是生成自制根证书;3、4是配置文件;5、6是用配置文件和根证书生成服务端的证书;在实践中发现,不用制作根证书(即:省略1、2步),直接用配置文件生成服务端的证书也可以正常使用。

总结一下步骤:

只需4步即可生成证书:

  1. 生成私钥 .key
    openssl genrsa -out [name].key 2048
  2. 通过 .key 生成 .csr证书申请文件
    openssl req -new -key [name].key -out [name].csr
    检查 & 打印 .csr
    openssl req -text -noout -verify -in [name].csr
  3. 通过 .key 和 .csr 生成 .crt证书
    openssl x509 -req -days 365 -in [name].csr -signkey [name].key -out [name].crt
    查看 .crt 内容
    openssl x509 -in [name].crt -text -noout
  4. 通过已有证书添加 v3.ext 重新生成证书
    v3.ext文件
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

最后的 DNS.1 = localhost 也可以直接写成 DNS = localhost。
openssl x509 -req -extfile v3.ext -days 3650 -in ssl.csr -CA ssl.crt -CAkey ssl.key -CAcreateserial -out ssl.crt
附1:不需要新建v3.ext文件的方法,直接把subjectAltName添加到证书里
openssl x509 -req -extfile <(printf "subjectAltName=DNS:localhost") -days 365 -in ssl.csr -CA ssl.crt -CAkey ssl.key -CAcreateserial -out ssl.crt
附2:一步到位法:通过 .cnf 配置文件生成证书
上面的步骤是先生成证书,再用添加v3.ext文件的方法添加subjectAltName;下面直接用配置文件生成证书,一步到位:
(1)、ssl.cnf

[req] 
prompt = no 
default_bits = 4096
default_md = sha256
distinguished_name = dn 
x509_extensions = v3_req

[dn] 
C=CN
ST=Shanghai
L=Shanghai
O=TEST
OU=Testing Domain
CN=localhost
emailAddress=admin@localhost

[v3_req]
keyUsage=keyEncipherment, dataEncipherment
extendedKeyUsage=serverAuth
subjectAltName=@alt_names

[alt_names]
DNS.1=localhost

(2)、通过 ssl.cnf 生成 .key、.crt
openssl req -new -newkey rsa:2048 -sha1 -days 180 -nodes -x509 -keyout myssl.key -out myssl.crt -config d:\ssl\ssl.cnf
(3)、通过 .cnf 文件生成 .csr
openssl req -new -config d:\ssl\ssl.cnf -key d:\ssl\myssl.key -out myssl.csr
(4)、查看生成的证书的命令:openssl x509 -in d:\ssl\myssl.crt -text -noout

openssl 总结:

openssl创建 .cnf 配置文件
openssl通过 .cnf 生成 .key、.crt
这两步只是生成了证书,要想在项目中配置、使用他们,还要做下面两步:

  • 生成服务端p12文件
    openssl pkcs12 -export -in d:\ssl\myssl.crt -inkey d:\ssl\myssl.key -out d:\ssl\myssl.p12 -name "server"
  • 把p12导入keystore
    keytool -importkeystore -v -srckeystore d:\ssl\myssl.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore d:\ssl\mytest.keystore -deststoretype jks -deststorepass 123456
  • tomcat里配置ssl,增加keystore
    并注意把证书导入系统,和浏览器中;
    hosts 文件注意 loalhost 要对应127.0.0.1

临时解决方案

如何让 Chrome 信任自签名证书:临时方案
不检查证书
chrome://flags/#allow-insecure-localhost

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容