前情提要
在開發的過程中經常會用到自簽發一些證書,比如寫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)配置文件.conf: 用戶可讀的一般配置文件,用於輔助生成請求文件
1) 私鑰.key:和證書中的公鑰是一組的
2)請求文件.csr: 給簽發工具使用,依此生成證書。依據配置文件生成並使用私鑰進行簽發的一個請求文件.
4)CA與根證書 .ca:
(1) CA:是指仲裁和簽發機構,用來簽發證書的。
(2)根證書, 可以代表CA的一個文件,無論是工具還是代碼可以用此來校驗證書的真偽(wxy: 只是合法性的驗證,別和解密的私鑰混淆)
5) 數字證書/證書/公鑰 .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. 其他文件的作用以及整個簽發過程
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
結果:問題解決