认证2 - 数字证书生成底层原理
前情提要
在开发的过程中经常会用到自签发一些证书,比如写https连接的程序, 经常使用的工具不外乎openssl等,步骤也都标准化...
但是,你是否有这样的疑惑,这每一步生成的到底是啥? 这些证书具体怎么用在https等协议中...
数字证书原理
参看另一篇博文:https://www.cnblogs.com/shuiguizi/p/13809942.html
X.509标准
根据认证的原理我们知道,需要有一个载体来承载这些身份信息,即数字证书。 但是,数字证书不是随便啥样就啥样,需要有一套标准。
其中,X.509标准就是一套这样的:签发流程, 证书格式等的规范。接下里我们以wiki为基准开始解析。
1. 原理概述
参考:https://en.wikipedia.org/wiki/X.509
是TLS/SSL协议中"握手"的一个具体的实现,他指定了证书的签发规范以及证书的形式.
在X.509里,组织机构通过发起证书签名请求(CSR)来得到一份签名的证书。具体的流程为:
首先,需要生成一个密钥对本地保存好,并用这个私钥对CSR进行数字签名。
然后,CSR中还要包含有关请求发起者的身份信息、以及用来验证签名的的公钥,以及证书所用的专有名称(DN)。CSR里还可能需要带有CA要求的其它有关身份证明的信息。
最后,由CA签发一个证书,证书中会将公钥绑定在DN上。
组织机构可以把受信的根证书分发给所有的成员,这样就可以使用公司的PKI系统了。浏览器(如Firefox)或操作系统预装有可信任的根证书列表,所以主流CA发布的TLS证书都直接可以正常使用。浏览器的开发者直接影响着它的用户对CA的信任。X.509也定义了CRL实现标准。另一种检查合法性的方式是OCSP。
所以,X509的证书内容以如下的形式进行呈现:
Certificate Version Number Serial Number Signature Algorithm ID Issuer Name Validity period Not Before Not After Subject name Subject Public Key Info Public Key Algorithm Subject Public Key Issuer Unique Identifier (optional) Subject Unique Identifier (optional) Extensions (optional) ... Certificate Signature Algorithm Certificate Signature
2. 证书的格式
上一章节中说明了证书的内容, 但是证书的载体是以什么格式存放的,比如是一张纸还是一块匾,比如是txt还是world等。
所以,在这一章节中,为上一节中定义的证书信息使用一种种格式保存起来,也就是证书的扩展名,支持的类型有:
.pem: 对DER证书进行Base64加密得到的证书格式(即PEM文件格式)
.cer, .crt, .der: 通常采用二进制DER形式,但再经过Base64编码的证书也很常见(即上面的.pem)
.p7b,.p7c - PKCS#7签名数据结构,而没有数据,只证书(S)或CRL(S)
.p12 – PKCS#12,可能包含证书(公共)和私钥(受密码保护)
.pfx – PFX,PKCS#12 的前身(通常包含 PKCS#12 格式的数据,例如,在IIS 中生成 PFX 文件)
这些扩展名对应的文件格式中,具体数怎样的要求呢?
openssl工具所支持的X.509标准下的证书, 有如下种类,当然这些格式的文件不仅仅服务于X509。
在执行openssl命令时,可以指定格式也可以是用缺省的,其表示的就是生成的证书格式,以及读入证书文件(比如作为过程文件被openssl读入), 支持的格式有如下:
- DER
根据 ASN.1 数据语言的可分辨编码规则(Distinguished Encoding Rules) 进行编码或解码的二进制格式.
- PEM
是 Base64 编码的二进制内容,再加上指定的开始行和结束行, 例如如下示例,,
Text before the BEGIN line is ignored. //这部分的内容将被忽略
----- BEGIN object-type -----
OT43gQKBgQC/2OHZoko6iRlNOAQ/tMVFNq7fL81GivoQ9F1U0Qr+DH3ZfaH8eIkX
xT0ToMPJUzWAn8pZv0snA0um6SIgvkCuxO84OkANCVbttzXImIsL7pFzfcwV/ERK
UM6j0ZuSMFOCr/lGPAoOQU0fskidGEHi1/kW+suSr28TqsyYZpwBDQ==
----- END object-type -----
Text after the END line is also ignored
object-type的取值必须与期望的object类型匹配. 比如以"BEGIN X509 CERTIFICATE"为头的证书,但是使用openssl命令时指定的参数却是要读取一个私钥,则不可以。支持的类型有:
ANY PRIVATE KEY
CERTIFICATE
CERTIFICATE REQUEST
...
wxy: 所谓object类型,可以理解为是干什么的文件, 比如是证书,还是请求,还是私钥...
- ENGINE
用于给OpenSSL engine指定加密物料。必须为一个engine配置或指定-engine选项。可以使用-passin选项向engine提供密码或 PIN 。
wxy: engine, 是OpenSSL中的一个引擎,即一个用于提供"加密以及解密的"服务引擎,重点在引擎上. 也就是说,此时的证书的不是独立的,需要结合引擎使用。
- P12
一个用DER编码的包含了PKCS#12 对象的文件。可能需要提供解密密码来获取私钥。
- SMIME
...
【小小结】:
X509定义了证书是什么样子,里面要包含了哪些数据信息.
证书需要怎么存放,是不是要加工一下,则由扩展名决定,确切的说是由签发的那个决定最终体现在扩展名上。
证书签发的实践
0. 概述
根据,上面的原理展示,核心目标是得到一个数字证书,确切的说是一组证书,因为对外提供的虽然是证书也成为公钥,但是内部还需要一个对应的私钥用于解析公钥。在生成之前,可能还需要一些指导生成的参数配置文件。核心步骤如下:
0. 配置文件
1) .conf: 用户可读的一般配置文件,用于辅助生成请求文件
2) 请求文件.csr: 给签发工具使用,依此生成证书。依据配置文件生成并使用私钥进行签发的一个请求文件.
1. 用于签发证书的CA机构。所谓的"机构"对应的其实也是一组证书,称为CA以及根证书 .ca:
1) CA:是指仲裁和签发机构,用来签发证书的。
2) 根证书, 可以代表CA的一个文件,无论是工具还是代码可以用此来校验证书的真伪(wxy: 只是合法性的验证,别和解密的私钥混淆)
2. 签发证书
1) 私钥.key:和证书中的公钥是一组的
2) 数字证书/证书/公钥 .crt: 目标
另外,文件后缀并不是固定如上,而是可以根据使用的工具以及制定的参数而定,比如如果还要进行一次base64加密,则格式将是.pem, 只不过里面的实际内容是标准的即可,比如openssl工具用 --key参数表示秘钥,所以这个文件无论是.key 还是xx.pem,其内容都必须是私钥这种object.
1. 详解CSR, certificate signing request
该文件是整个证书签发过程的第一步,需要在与安装证书的同一台服务器上生成的, CSR 包含了一些证书颁发机构 (CA) 用于签发证书需要的信息(例如通用名称、组织、国家/地区。还包括将用在证书中的公钥信息,并且使用了相应的私钥进行签名
csr中需要包含哪些信息?
1). 身份信息
Common Name (CN):The fully qualified domain name (FQDN) of your server.
Organization (O): The legal name of your organization. Do not abbreviate and include any suffixes, such as Inc., Corp., or LLC.
For EV and OV SSL Certificates, this information is verified by the CA and included in the certificate.
Organizational Unit(OU): The division of your organization handling the certificate.
...
2). 需要包含在证书中的公钥
证书不等于公钥,公钥是用来加密的,私钥是用来解密的, 在ssl握手中,对方通过之前取得的证书中的公钥对数据进行加密,我方通过自己保留的私钥进行解密.
3). 关于密钥类型和长度的信息。
最常见的密钥大小是 RSA 2048,但一些 CA,包括 GlobalSign,支持更大的密钥大小(例如 RSA 4096+)或 ECC 密钥。
样例,以pem格式为例
----- BEGIN NEW CERTIFICATE REQUEST -----
MIIDVDCCAr0CAQAweTEeMBwGA1UEAxMVd3d3Lmpvc2VwaGNoYXBtYW4uY29tMQ8w
...
如何创建CSR
尽管说csr是整个证书签发过程的第一步,但实际上这个文件也是需要使用工具创建的,
wxy: 这个文件可以理解为是用来呈现用户定制化,证书是一个具有固定格式的正式文件,不可以手写,里面可以包含一些你的身份信息以及你想要呈现或者想要对外宣布的其他信息都是啥,就需要先提供好,然后用工具根据你的信息生成出来证书等....
2. 其他文件
1)v3扩展文件
在X.509证书中,v3扩展文件用于定义和存储额外的扩展信息,以增强证书的功能和灵活性。X.509 v3扩展允许在证书中添加各种类型的附加信息,这些信息可以提供关于证书用途和限制的更详细的说明。主要用途如下:
-
用途约束 (Key Usage and Extended Key Usage):
- Key Usage:指定公钥的基本用途,如数字签名、密钥加密、证书签名等。
- Extended Key Usage:进一步细化公钥的用途,如服务器认证、客户端认证、代码签名等。
-
主题替代名称 (Subject Alternative Name, SAN):
- 允许指定除常用名称 (CN) 之外的其他身份标识,如电子邮件地址、DNS名称、IP地址等。
-
颁发者替代名称 (Issuer Alternative Name):
- 提供证书颁发者的其他身份标识。
-
CRL分发点 (CRL Distribution Points):
- 指定证书撤销列表 (CRL) 的位置,以便验证证书是否被撤销。
-
权限信息访问 (Authority Information Access, AIA):
- 包含有关如何访问与证书相关的颁发者信息的URL,例如颁发者的OCSP (Online Certificate Status Protocol) 服务器地址。
-
证书策略 (Certificate Policies):
- 定义证书所遵循的策略标识符,并可能包括策略的详细说明。
-
基本约束 (Basic Constraints):
- 指示证书是否是CA证书以及可以签署多少级下级证书。
示例:
[ v3_req ] basicConstraints = CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment subjectAltName = @alt_names [ alt_names ] DNS.1 = example.com DNS.2 = www.example.com
- basicConstraints:指示该证书不是CA证书。
- keyUsage:指定公钥的用途,包括数字签名、不可否认性、密钥加密和数据加密。
- subjectAltName:定义了两个备用DNS名称。
使用方法
在创建证书请求或自签名证书时,可以指定扩展配置文件。例如:
openssl req -new -key private.key -out request.csr -config openssl.cnf -extensions v3_req
openssl x509 -req -in request.csr -signkey private.key -out certificate.crt -extfile v3.ext
3. 整个签发过程
0). openssl命令介绍
openssl the OpenSSL command line tool, a swiss army knife for cryptographic tasks, testing and analyzing. It can be used for
creation of key parameters
creation of X.509 certificates, CSRs and CRLs
calculation of message digests
encryption and decryption
SSL/TLS client and server tests
handling of S/MIME signed or encrypted mail
and more...
1) .准备csr使用的配置文件,例如:
cat <<EOF >> ${tmpdir}/csr.conf [req] req_extensions = v3_req distinguished_name = req_distinguished_name [req_distinguished_name] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = ${service} DNS.2 = ${service}.${namespace} DNS.3 = ${service}.${namespace}.svc EOF
2) 生成秘钥(对):
方式一:openssl genrsa(废弃):用于生成 RSA私钥
常用参数:
-des,-des3.../-cipher des3 : 给私钥加密使用的加密cipher(密码),不指定则表示不加密
例如:openssl genrsa -out ${tmpdir}/server-key.pem 2048
方式二:openssl-genpkey:用于生成指定算法类型的私钥
-algorithm alg : 公钥生成使用的算法,支持RSA, DSA, DH or DHX,
3) 生成CSR
openssl req
常用参数
-config filename: csr中需要的信息要么在执行这个命令时使用交互方式输入,要么直接使用配置文件写好
-days n:number of days to certify the certificate for, The default is 30 days
-key filename|uri: 指定私钥文件
-newkey arg: 生成一个新的csr以及新的私钥
-new: 生成一个新的csr,如果未指定-key参数,则同样会生成新的私钥
例如:
openssl req -new -key server-key.pem -subj "/CN=service.namespace.svc" -out server.csr -config csr.conf
4)生成根证书, 实际也是一个普通的证书
The x509 utility can be used to sign certificates and requests: it can thus behave like a "mini CA".
常用参数:
-signkey filename: this option causes the input file to be self signed using the supplied private key.
例如:
1)
# openssl genrsa -out ca.key 2048
2)
方式一:
# openssl req -new -key ca.key -out ca.csr
# openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
方式二:
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
或
openssl req -new -x509 -days 365 -key ca.key -out ca.crt subj "/C=GB/L=London/O=xx/CN=www.wxy.com
说明:
在标准的证书签发流程中, 首先要有一个第三方的仲裁机构称为CA, 与其对应的也存在一个证书称为根证书,
之后, 用户的签发请求实际上就是向CA机构发请求, 然后CA会用自己的根证书签发一个用户证书,
所以,在这里我们同样模拟出一个CA证书即根证书,这个证书为自签发(即, 签发机构也是自己,因为自己此时就是始祖...)
另外, 有些应用场景可以省略步骤, 直接将自签发的证书作为用户证书.
4)正式签发证书
openssl req -x509
常用参数
...同上面生成csq..
-in filename: the input filename to read a request from, 如果不指定,则从标准输入中读取。如果不指定-new or -newkey参数,则请求唯一从该文件中读入.
-nodes/-noenc: If this option is specified then if a private key is created it will not be encrypted.
-CA filename|uri: 使用指定的ca为证书签名,一旦指定该参数隐含使用-x509
-CAkey filename|uri:Sets the "CA" private key to sign a certificate with. The private key must match the public key of the certificate given with -CA. If this option is not provided then the key must be present in the -CA input. (wxy: 没彻底弄清楚这个文件的作用)
例如:
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey server.key -CAcreateserial -out server.crt
openssl req -x509 -nodes -days 2920 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=rancher.test.org/O=nginxsvc"
3. 使用已存在ca证书签发的全过程
openssl genrsa -out wxy.key 2048
openssl req -new -key wxy.key -out wxy.csr -subj "/CN=see"
openssl x509 -req -in wxy.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out see.crt -days 3650
三:扩展:
扩展1:
已存在的ca.crt,但是目标需要的是a PEM encoded CA, 即经过一下base64加密,所以
方式1:直接base64加密
#cat ${tmpdir}/ca.crt | base64 | tr -d '\n'
方式2:利用openssl命令
openssl base64 -A -in ca.crt -out ca.pem
扩展2:在k8s中
如果想要将k8s的pki作为权威机构, 我们可以生成好自己的csr,然后让k8s帮我们签发证书,具体的步骤为
1. 准备配置文件csr.conf, 同上
2. 生成自己的私钥server-key.pem, 同上
3. 生成csr文件, 注意,这里面的CN可以看出就是svc的域名
openssl req -new -key ${tmpdir}/server-key.pem -subj "/CN=${service}.${namespace}.svc" -out ${tmpdir}/server.csr -config ${tmpdir}/csr.conf
4.创建一种k8s的object:CertificateSigningRequest/csr, 用于将签发请求发送给apiserver
cat <<EOF | kubectl create -f - apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: ${csrName} spec: groups: - system:authenticated request: $(cat ${tmpdir}/server.csr | base64 | tr -d '\n') usages: - digital signature - key encipherment - server auth EOF
5.执行 approved进行签发
kubectl certificate approve ${csrName}
其中,csr的.status.certificate字段正式证书信息
serverCert=$(kubectl get csr ${csrName} -o jsonpath='{.status.certificate}')
6. 为证书做base64加密然后转化成pem格式
echo ${serverCert} | openssl base64 -d -A -out ${tmpdir}/server-cert.pem
7. 将整个证书信息放到secret中保存
kubectl create secret generic ${secret} \ --from-file=tls.key=${tmpdir}/server-key.pem \ --from-file=tls.crt=${tmpdir}/server-cert.pem \ --dry-run=client -o yaml | kubectl -n ${namespace} apply -f -
8. 获取签发的根证书,即集群的根证书
export CA_BUNDLE=$(kubectl config view --raw --flatten -o go-template='{{range .clusters}}{{if eq .name "kubernetes"}}{{index .cluster "certificate-authority-data"}}{{end}}{{end}}')
或者
k get secret -ngrafana-operator default-token-mzjvl -oyaml
==============
一. 准备根证书
# openssl genrsa -out ca.key 2048
# openssl req -new -key ca.key -out ca.csr
# openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
想要pem格式的,即变成bas64的,则
openssl base64 -A -in ca.crt -out ca.pem
二. 准备证书签发请求
#openssl genrsa -out server.key 2048
# openssl req -new -key server.key -out server.csr
三. 正式签发
# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
爬坑:
1. cannot validate certificate for 192.168.95.143 because it doesn't contain any IP SANs
原因:
尝试1:
# openssl req -new -key server.key -out server.csr -subj "/CN=192.168.95.143"
# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
结果:not ok
尝试2:
openssl req -new -key server.key -out server.csr -subj "/CN=192.168.95.143"
echo subjectAltName = IP:192.168.95.143 > extfile.cnf
# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile extfile.cnf -out server.crt
Signature ok
subject=/CN=192.168.95.143
Getting CA Private Key
结果:问题解决