TLS证书与自签ca


自签CA流程

自签CA流程如图

公网的CA机构签发证书的流程和原理基本也是如此,不过根CA的压力会比较大,所以延伸出了二级ca等,以此保证证书链的完整和有效。

一般的操作系统出厂时都会内置可用的公网CA的证书拉作为信任证书源,以保证用户访问的网站使用新签发的证书时,通过证书验证。

所以,一般在双向认证场景下,我们可以只用一套根CA,不断为新的服务签发证书,每个服务只信任根CA签发的证书。

Keytool简介

JDK自带的keytool是一个证书工具,位于\bin\keytool.exe(linux同理),用它也可以生成ssl证书,但笔者觉得openssl更好用些,keytool用于p12格式和jks格式密钥库的转换就行了

使用 keytool -help 查看可用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-certreq            生成证书请求
-changealias 更改条目的别名
-delete 删除条目
-exportcert 导出证书
-genkeypair 生成密钥对
-genseckey 生成密钥
-gencert 根据证书请求生成证书
-importcert 导入证书或证书链
-importpass 导入口令
-importkeystore 从其他密钥库导入一个或所有条目
-keypasswd 更改条目的密钥口令
-list 列出密钥库中的条目
-printcert 打印证书内容
-printcertreq 打印证书请求的内容
-printcrl 打印 CRL 文件的内容
-storepasswd 更改密钥库的存储口令

使用keytool -command_name -help查看命令用法。

keystore 和 truststore

Keytool 将密钥(key)和证书(certificates)存在一个称为 keystore 的文件中,在 keystore 里,包含两种数据:

  1. 密钥实体(Key entity)— 密钥(secret key)又或者是私钥和配对公钥(采用非对称加密),包含了私钥,所以是一个需要保密的文件
  2. 可信任的证书实体(trusted certificate entries)— 只包含公钥

证书库中的一条证书可以导出数字证书文件,数字证书文件只包括主体信息和对应的公钥;

keystore 和 truststore 从其文件格式来看其实是一个东西,只是为了方便管理将其分开;

keystore 中一般保存的是我们的私钥,用来加解密或者为别人做签名。

truststore 中保存的是一些可信任的证书,主要是 java 在代码中访问某个 https 的时候对被访问者进行认证的,以确保其实可信任的。

truststore 里存放的是只包含公钥的数字证书,代表了可以信任的证书,而 keystore 是包含私钥的;

生成密钥库

密钥库可以当作证书仓库来看

keytool -genkeypair [option]keytool -genkey [option]

选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-alias <alias>                  要处理的条目的别名
-keyalg <keyalg> 密钥算法名称
-keysize <keysize> 密钥位大小
-sigalg <sigalg> 签名算法名称
-destalias <destalias> 目标别名
-dname <dname> 唯一判别名
-startdate <startdate> 证书有效期开始日期/时间
-ext <value> X.509 扩展
-validity <valDays> 有效天数
-keypass <arg> 密钥口令
-keystore <keystore> 密钥库名称
-storepass <arg> 密钥库口令
-storetype <storetype> 密钥库类型
-providername <providername> 提供方名称
-providerclass <providerclass> 提供方类名
-providerarg <arg> 提供方参数
-providerpath <pathlist> 提供方类路径
-v 详细输出
-protected 通过受保护的机制的口令

其中:

  • -storetype 指定仓库类型, JKS、 JCEKS、 PKCS12等,默认JKS
  • -keyalg 指定密钥的算法, RSA、 DSA 等,默认DSA
  • -keysize 指定密钥长度,默认2048
  • -alias 指定密钥对的别名,该别名是公开的
  • -keystore 密钥库的路径及名称

例:

1
2
3
4
# 根据提示挨个输入参数
keytool -genkey -alias <密钥库别名> -storetype <密钥库类型> -keyalg <密钥算法名称> -keystore <密钥库路径>
# 输入参数完毕后将在当前路径生成证书
keytool -genkey -alias test -storetype jks -keyalg RSA -keystore test-keystore.jks
1
2
3
4
# 一次性搞定
keytool -genkey -alias <密钥库别名> -storetype <密钥库类型> -keystore <密钥库路径> -keyalg <密钥算法名称> -keysize <密钥位大小> -storepass <密钥库口令> -keypass <密钥口令> -validity <有效天数> -dname <dname>
# 直接在当前路径生成证书,CN可填多个如:"CN=test1.com, CN=test2.com, OU=test.com, O=ChinaHonker, L=Beijing, S=Beijing, C=086"
keytool -genkey -alias ws-ssl-server-keystore -storetype jks -keystore ws-ssl-server-keystore.jks -keyalg RSA -keysize 2048 -storepass 123456serverstore -keypass 123456serverkey -validity 3650 -dname "CN=WindShadow, OU=WindShadow,O=WS-Server, L=Beijing, S=Beijing, C=086"

dname详解

  • CN:Common Name 公用名称,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名
  • OU:Organization Name 单位名称,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端单位证书则为证书申请者所在单位名称
  • O:Organization 组织
  • L: Locality 所在城市
  • S:State 所在省份
  • C:Country 所在国家,只能填代表国家的双字母或地区代码,如中国:CN或086

导出证书(.cer文件)

1
2
keytool -keystore <密钥库路径> -alias <密钥库别名> -storepass <密钥库口令> -export -file <证书路径>
keytool -keystore ws-ssl-server-keystore.jks -alias ws-ssl-server-keystore -storepass 123456serverstore -export -file ws-ssl-server-keystore.cer

导入证书(.cer文件)到密钥库

1
2
keytool -import -alias <密钥库别名> -storepass <密钥库口令> -file <证书路径> -keystore <密钥库路径>
keytool -import -alias ws-ssl-server-keystore -storepass 123456serverstore -file ws-ssl-server-keystore.cer -keystore ws-ssl-trust-keystore.jks

转换密钥库格式

jks转pkcs12,p12的密钥库其实不需要使用keypass,但是使用keytool进行转换还是要指定一下参数

1
2
3
4
5
keytool -importkeystore \
-srckeystore <源密钥库路径> -srcstoretype <源密钥库类型> -srcalias <源密钥库别名> -srcstorepass <源密钥库口令> -srcstorepass <源密钥口令> \
-destkeystore <目标密钥库路径> -deststoretype <目标密钥库类型> -destalias <源密钥库别名> -deststorepass <目标密钥库口令> -destkeypass <目标密钥口令>

keytool -importkeystore -srckeystore ws-ssl-server-keystore.jks -srcstoretype jks -srcalias ws-ssl-server-keystore -srcstorepass 123456serverstore -srckeypass 123456serverkey -destkeystore ws-ssl-server-keystore.p12 -deststoretype pkcs12 -destalias ws-ssl-server-keystore -deststorepass 123456serverstore -destkeypass 123456serverkey

列出密钥库条目

1
2
3
keytool -list -keystore  <密钥库路径>  -storepass <密钥库口令> -keypass <密钥口令>

keytool -list -keystore ws-ssl-server-keystore.jks -storepass 123456serverstore -keypass 123456serverkey

openssl自签ca示例

cagen.sh脚本(Linux),用到的openssl配置文件可从此地址下载:http://web.mit.edu/crypto/openssl.cnf

1
2
3
.
├── cagen.sh
└── openssl.cnf

cagen.sh

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
### rootca
readonly rootca="ws-rootca"
readonly rootcaKeyPwd="rootcaKeyPwd"
readonly rootDname="/CN=WindShadow-root/OU=WindShadow/O=WS/L=Beijing/S=Beijing/C=CN"
readonly opensslCnf="openssl.cnf"
readonly globalValidity=3650

readonly trustKeystore="ws-trust"
readonly trustKeystorePwd="ws-trustKeystorePwd"

function clean() {

rm -f *.p12
rm -f *.jks
rm -f *.cer
rm -f *.csr
rm -f *.crt
rm -f *.key
rm -f *.pem
rm -f *.crl
rm -f *.pass
rm -rf ./demoCA
rm -rf ./gen
}

function CrtToP12() {

crtFile=${1}
keyFile=${2}
keyPwd=${3}
keystore=${4}
keystoreAlias=${5}

openssl pkcs12 -export -clcerts \
-in ${crtFile} \
-inkey ${keyFile} -passin pass:${keyPwd} \
-out ${keystore} -name ${keystoreAlias} -password pass:${keyPwd}
}

function CrtToCer() {

crtFile=${1}
cerFile=${2}
openssl x509 -in ${crtFile} -out ${cerFile} -outform der
}

function genRootCA() {

### openssl genrsa -aes256 -passout pass:${keyPwd} -out ${caName}.key 2048 # 加密为pkcs1的格式
### pkcs1 转 pkcs8 要先解密再加密。即指定 -passin 和 -passout

# 无加密
openssl genrsa -out ${rootca}.key 2048
# 加密为 pkcs8
openssl pkcs8 -topk8 -in ${rootca}.key -out ${rootca}.key.e -passout pass:${rootcaKeyPwd} \
&& rm -f ${rootca}.key \
&& mv ${rootca}.key.e ${rootca}.key
openssl req -new -key ${rootca}.key -passin pass:${rootcaKeyPwd} \
-out ${rootca}.csr \
-days ${globalValidity} \
-subj ${rootDname}

openssl x509 -req \
-in ${rootca}.csr \
-signkey ${rootca}.key -passin pass:${rootcaKeyPwd} \
-extfile ${opensslCnf} -extensions v3_ca \
-out ${rootca}.crt \
-days ${globalValidity}

CrtToCer ${rootca}.crt ${rootca}.cer
rm -f ${rootca}.csr
}

function rootCAIssue() {

caName=${1}
keyPwd=${2}
dname=${3}
validity=${4}

openssl genrsa -out ${caName}.key 2048
openssl pkcs8 -topk8 -in ${caName}.key -out ${caName}.key.e -passout pass:${keyPwd} \
&& rm -f ${caName}.key \
&& mv ${caName}.key.e ${caName}.key
openssl req -new -key ${caName}.key -passin pass:${keyPwd} \
-out ${caName}.csr \
-days ${validity} \
-subj ${dname}

openssl x509 -req \
-in ${caName}.csr \
-CAkey ${rootca}.key -passin pass:${rootcaKeyPwd} \
-CA ${rootca}.crt -CAcreateserial \
-extfile ${opensslCnf} -extensions v3_ca \
-out ${caName}.crt \
-days ${validity}

rm -f ${caName}.csr
}

function add2Trust() {

caName=${1}
caFile=${2}
keytool -importcert -trustcacerts -deststoretype pkcs12 -alias ${caName} -keystore ${trustKeystore}.p12 -storepass ${trustKeystorePwd} -file ${caFile} -noprompt
}

function main() {

### serverca
readonly serverca="ws-serverca"
readonly servercaKeyPwd="ws-servercaKeyPwd"
readonly serverDname="/CN=WindShadow-server/OU=WindShadow/O=WS/L=Beijing/S=Beijing/C=CN"
rootCAIssue ${serverca} ${servercaKeyPwd} ${serverDname} ${globalValidity}

###
CrtToP12 ${rootca}.crt ${rootca}.key ${rootcaKeyPwd} ${rootca}.p12 ${rootca}
CrtToP12 ${serverca}.crt ${serverca}.key ${servercaKeyPwd} ${serverca}.p12 ${serverca}

### 高版本jdk的keytool的p12密钥库在低版本JDK使用时不兼容

# 导入rootCa根证书到信任库中
add2Trust ${rootca} ${rootca}.cer

### 整理
cp ${rootca}.crt ${trustKeystore}.crt
mkdir -p gen/ca/
mkdir -p gen/certificate/
mkdir -p gen/trust/
mv ${rootca}.* gen/ca/
mv ${serverca}.* gen/certificate/
mv ${trustKeystore}.* gen/trust/

}
set -x
clean
genRootCA
main

得到以下文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gen
├── ca
│   ├── ws-rootca.cer
│   ├── ws-rootca.crt
│   ├── ws-rootca.key
│   ├── ws-rootca.p12
│   └── ws-rootca.srl
├── certificate
│   ├── ws-serverca.crt
│   ├── ws-serverca.key
│   └── ws-serverca.p12
└── trust
├── ws-trust.crt
└── ws-trust.p12

serverca可以部署到服务上作为服务端证书,trust可作为双向认证时的可信证书(crt)或可信密钥库(p12),保存rootca可用于新服务证书的签发