通过openssl
创建CA证书 第一步是创建一个秘钥,这个便是CA证书的根本,之后所有的东西都来自这个秘钥:
# 通过rsa算法生成2048位长度的秘钥
openssl genrsa -out myCA.key 2048
第二步是通过秘钥加密机构信息形成公钥:
# 公钥包含了机构信息,在输入下面的指令之后会有一系列的信息输入,这些信息便是机构信息,公司名称地址什么的
# 这里还有一个过期信息,CA证书也会过期,openssl默认是一个月,我们直接搞到100年
openssl req -new -x509 -key myCA.key -out myCA.cer -days 36500
这一步需要输入的机构信息有点,分别说一下:
参数名称
参数值
Country Name
国家代码,比如中国就是CN
State or Province Name
省名称
Locality Name
城市名称
Organization Name
机构名称
Organizational Unit Name
机构单位名称
Common Name
重点参数 :授权给什么,因为机构是根节点所以是授权给自己
Email Address
邮件地址
通过windows域控创建CA证书 这种便是我采用的方案,执行上比直接用openssl
创建证书复杂多了,但是好处也非常多,一方面域控下级的所有计算机天然对域控服务就是信任状态,第二是域控制器能够通过组策略域内同步CA证书,本质上来讲相对于多了一个CA证书同步与分发的机制。我这边使用的Windows Server 2016,其他版本区别也不大。
第一步、在域控上启用证书服务
服务器角色 必选Active Directory 证书服务
角色服务选 【证书颁发机构】 【证书颁发机构Web注册】
第二步、安装完毕之后配置证书
非常简单,直接根据提示输入相关信息就行了,在过期时间那一步最好将时间拉长,建议使用的100年。
第三步、通过组策略进行分发
策略路径是:计算机策略/Windows设置/安全设置/公钥策略/受信任的根证书颁发机构
和计算机策略/Windows设置/安全设置/公钥策略/受信任的发布者证书
。将上面创建的证书导出之后,在这里导入即可。
创建服务器证书 在得到CA证书之后,需要通过openssl
工具对证书进行转换得到公钥(.crt文件
)和密钥(.key文件
),无论CA证书是怎么来的,到这里之后就没有任何区别了,服务器证书的制作流程相较CA证书要复杂一点点。
第一步通过openssl
工具创建服务器的秘钥:
# 通过RSA算法生成长度2048位的秘钥
openssl genrsa -out server.key 2048
第二步这里是创建一个签名请求
需要将服务器信息写入到请求文件之中,然后通过CA机构证书对请求签名形成服务器证书公钥,这一步要复杂一些,很多网上的教程在这里都GG了主要原因没有把原理搞清楚。
首先https
证书的公钥不同于自定义情况下的加密证书,这里需要安装浏览器标准进行配置,首先openssl
默认的证书版本是V1,V1在支持https
时部分浏览器依旧会认为不安全,所以需要使用V3版本;同时openssl
即便是使用V3版本依旧没有附带V3的subjectAltName
字段数据(这里是证书对应的IP地址或者域名,可以用通配符)。但是这些东西命令行没法指定所以需要配置文件,我这里准备了一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 [ ca ] default_ca = CA_default [ CA_default ] dir = ./demoCA certs = $dir/certs crl_dir = $dir/crl database = $dir/index.txt new_certs_dir = $dir/newcerts certificate = $dir/cacert.pem serial = $dir/serial crlnumber = $dir/crlnumber crl = $dir/crl.pem private_key = $dir/private /cakey.pem RANDFILE = $dir/private /.rand x509_extensions = usr_cert name_opt = ca_default cert_opt = ca_default default_days = 365 default_crl_days= 30 default_md = default preserve = no policy = policy_match [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] default_bits = 1024 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca string_mask = utf8only req_extensions = v3_req [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = CN countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = BeiJing localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) 0.organizationName_default = myca organizationalUnitName = Organizational Unit Name (eg, section) commonName = Common Name (e.g. server FQDN or YOUR name) commonName_max = 64 emailAddress = Email Address emailAddress_max = 64 [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ usr_cert ] basicConstraints=CA:FALSE nsCertType = client, email, objsign keyUsage = nonRepudiation, digitalSignature, keyEncipherment nsComment = "OpenSSL Generated Certificate" subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer [ svr_cert ] basicConstraints=CA:FALSE nsCertType = server keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer extendedKeyUsage = serverAuth,clientAuth [ v3_req ] subjectAltName = @alt_names \ \ [ alt_names ] DNS.1 = xunshi.com DNS.2 = *.xunshi.com IP.1 = 192.168.0.2 IP.2 = 192.168.0.3 [ v3_ca ] subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = CA: true [ crl_ext ] authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] basicConstraints=CA:FALSE nsComment = "OpenSSL Generated Certificate" subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo [ tsa ] default_tsa = tsa_config1 [ tsa_config1 ] dir = ./demoCA serial = $dir/tsaserial crypto_device = builtin signer_cert = $dir/tsacert.pem \ certs = $dir/cacert.pem \ signer_key = $dir/private /tsakey.pem default_policy = tsa_policy1 \ other_policies = tsa_policy2, tsa_policy3 digests = md5, sha1 accuracy = secs:1, millisecs:500, microsecs:100 clock_precision_digits = 0 ordering = yes \ tsa_name = yes \ ess_cert_id_chain = no \
将上面的配置内容保存为openssl.cnf
放到生成的服务器证书文件的目录下(注意 :修改alt_names里面的域名或者IP为最终部署需要的地址,支持通配符),然后执行创建签名申请文件即可,执行运行:
# 和创建CA时一样这里需要输入一堆服务器信息,输入项也是相同的。
# 不过在输入Common Name(CN)最好直接输入服务器的IP地址或者域名。
openssl req -config openssl.cnf -new -out server.req -key server.key
PS:上述配置文件使用sha1算法生产的证书,部分浏览器已经已经不信任该算法了,如果你使用的时候遇到sha1相关的问题,可以参考评论区的kevin 同学提供的方案 。
如果你遇到sha1问题,用稍微新一点的openssl.cnf文件 https://github.com/openssl/openssl/blob/master/apps/openssl.cnf 同时还要在这个文件里稍微改一下,把下述的配置加入进去 “ [ v3_req ] subjectAltName = @alt_names \# 这里是重点,需要将里面配置为最终服务端需要的域名或者IP \# 这里可以写多个,能够自行添加DNS.X = XXXXXX [ alt_names ] DNS.1 = xunshi.com DNS.2 = *.xunshi.com “
加上。 最后用请求生成密钥的时候 用下面这个指令 使用sha384代替默认的sha1。
第三步通过CA机构证书对服务器证书进行签名认证
# 这里没有什么需要说的,本质上就是将签名请求文件进行签名最终得到服务器的公钥
openssl x509 -req -extfile openssl.cnf -extensions v3_req -in server.req -out server.cer -CAkey myCA.key -CA myCA.cer -days 36500 -CAcreateserial -CAserial serial
第四步部署证书
这里应该没有什么需要说的了,我们通过Nginx部署,最终得到server.key
就是秘钥,server.cer
文件就是公钥只需要配置给Nginx就行了。
信任CA机构证书 如果通过Windows域控创建的CA证书,其证书本身通过组策略便可以给每一个域下计算机添加机构信任。如果你没有域控只是通过openssl
创建的CA证书也没有关系,只需要将CA证书的公钥(myCA.cer文件
)导入到系统信任的根证书颁发机构里面就行了:
操作界面在windows的internet选型->内容->证书
可以打开,导入即可,也可以直接双击cer
文件进行证书安装,最终不光是windows系统,任何操作系统都可以安装证书来进行对CA机构的进行信任操作。
在对证书进行信任之后通过https打开浏览器进入内网DNS
或者host
配置的域名便可以得到没有任何警告的内容的安全连接:
如果是Mac系统访问逻辑也是一样的通过安装CA证书并且在钥匙串内添加信任之后依然可以正常访问:
在Android手机上也是一样,安装并且信任证书之后可以正常访问:
总结 本来对我对https
的认证逻辑其实理解没有多深入,以前也只是用过SSL证书进行TCP传输加密而已,经过对openssl
的学习现在至少在理解上达到了及格水平,不过这次学习论证与探索的过程我个人极其不愉快,本来这东西在有了理解之后大家都看得出来不是什么很难的东西,事实上我也只用了一天半就搞定了。但是网上充斥大量垃圾内容,不光没有什么正向内容甚至不少内容还TM起了误导的作用,整个中文互联网检索体系下就没有找到一篇文章稍微详细描述整个搭建逻辑与流程,简直了,最终我只能从https
原理和openssl
的官方文档开始看起,过于离谱了。基本上可以得到一个结论现在天天写一些所谓干货的博主简直就是滥竽充数,其内容千篇一律大多数也是抄袭来的基本上什么都没有说清楚简直浪费时间。
最后说一下https
的原理,在解释清楚之后其实不是绝对上的安全,结合本文各位可以想一下怎样去伪造一个页面出来?假设我是黑客来搞入侵其实只需要一个小小的脚本就可以了,我们自行制作CA和服务证书之后,通过修改HOST文件对域名解析进行劫持将其引导到我们自己的服务器,然后将我们自己制作CA证书注入目标电脑的受信任证书组,这样一来对于被入侵者已经看到是安全连接但是其请求已经被我们拦截了。所以各位不要看到https就以为安全了,一旦你的电脑本身就被入侵了那么https
也是形同虚设的,所以在执行高风险操作的时候最好还是点开站点的证书看看对应的CA机构是不是被修改过。