背景
在做一个区块链项目时,区块链底层平台采用PBFT共识,没有配置块的概念,fabric有配置块约定链的权限。当删除一个节点时由于证书还没有吊销,被删除节点还可以自己启动加入区块链网络,这里实现证书吊销十分有必要。
x509证书吊销
什么是证书吊销
证书吊销列表 (Certificate Revocation List ,简称: CRL) 是 PKI 系统中的一个结构化数据文件,该文件包含了证书颁发机构 (CA) 已经吊销的证书的序列号及其吊销日期。 CRL 文件中还包含证书颁发机构信息、吊销列表失效时间和下一次更新时间,以及采用的签名算法等。证书吊销列表最短的有效期为一个小时,一般为 1 天,甚至一个月不等,由各个证书颁发机构在设置其证书颁发系统时设置。
证书吊销列表(CRL)与证书状态在线查询协议(OCSP)
讲到吊销列表,就不得不讲讲 OCSP ,Online Certificate Status Protocol, 证书状态在线查询协议, 是 IETF 颁布的用于实时查询数字证书在某一时间是否有效的标准。
上面已经提到,一般 CA 都只是 每隔一定时间 ( 几天或几个月 ) 才发布新的吊销列表,可以看出: CRL 是 不能及时反映证书的实际状态的。而 OCSP 就能满足实时在线查询证书状态的要求。它为电子商务网站提供了一种实时检验数字证书有效性的途径,比下载和处理 CRL 的传统方式更快、更方便和更具独立性。请求者发送查询请求, OCSP 服务器会返回证书可能的三个状态:正常、吊销和未知。
OCSP 服务由独立的 OCSP 服务器来提供服务,目前WoSign新证书颁发系统支持 OCSP 方式查询证书状态。 OCSP 也是一个可以访问的 http 网站。
证书吊销作用
在服务程序使用到证书时,一定会先检查此证书是否已经被吊销,也就是说会查询吊销列表或 OCSP 服务, 如果此证书已经被证书颁发机构吊销,则认为持有该证书的角色为非法的,不会进行后续的操作。
证书吊销原理
一般来讲CA会有证书吊销的接口,下文中会通过openssl做一些自建CA,签发证书,吊销证书,创建CRL等操作。创建CRL最终会生成.crl文件,在验证证书时,会检测证书是否在.crl文件里,即判断是否已经注销。
例如x509上,创建CRL函数:
func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
}
fabric上关于CRL的操作
1、从crl目录读取crl列表到msp对象
func (msp *bccspmsp) setupCRLs(conf *m.FabricMSPConfig) error {
// setup the CRL (if present)
msp.CRL = make([]*pkix.CertificateList, len(conf.RevocationList))
for i, crlbytes := range conf.RevocationList {
crl, err := x509.ParseCRL(crlbytes)
if err != nil {
return errors.Wrap(err, "could not parse RevocationList")
}
// TODO: pre-verify the signature on the CRL and create a map
// of CA certs to respective CRLs so that later upon
// validation we can already look up the CRL given the
// chain of the certificate to be validated
msp.CRL[i] = crl
}
return nil
}
2、在validateIdentity、validateCAIdentity、validateTLSCAIdentity时会查询CRL并做验证
func (msp *bccspmsp) validateCertAgainstChain(cert *x509.Certificate, validationChain []*x509.Certificate) error {
// here we know that the identity is valid; now we have to check whether it has been revoked
// identify the SKI of the CA that signed this cert
SKI, err := getSubjectKeyIdentifierFromCert(validationChain[1])
if err != nil {
return errors.WithMessage(err, "could not obtain Subject Key Identifier for signer cert")
}
// check whether one of the CRLs we have has this cert's
// SKI as its AuthorityKeyIdentifier
for _, crl := range msp.CRL {
aki, err := getAuthorityKeyIdentifierFromCrl(crl)
if err != nil {
return errors.WithMessage(err, "could not obtain Authority Key Identifier for crl")
}
// check if the SKI of the cert that signed us matches the AKI of any of the CRLs
if bytes.Equal(aki, SKI) {
// we have a CRL, check whether the serial number is revoked
for _, rc := range crl.TBSCertList.RevokedCertificates {
if rc.SerialNumber.Cmp(cert.SerialNumber) == 0 {
// We have found a CRL whose AKI matches the SKI of
// the CA (root or intermediate) that signed the
// certificate that is under validation. As a
// precaution, we verify that said CA is also the
// signer of this CRL.
err = validationChain[1].CheckCRLSignature(crl)
if err != nil {
// the CA cert that signed the certificate
// that is under validation did not sign the
// candidate CRL - skip
mspLogger.Warningf("Invalid signature over the identified CRL, error %+v", err)
continue
}
// A CRL also includes a time of revocation so that
// the CA can say "this cert is to be revoked starting
// from this time"; however here we just assume that
// revocation applies instantaneously from the time
// the MSP config is committed and used so we will not
// make use of that field
return errors.New("The certificate has been revoked")
}
}
}
}
return nil
}
证书吊销应用
这里主要采用openssl来自建一套CA,进行证书签发,吊销等操作
一、查看openssl配置
cat /usr/local/etc/openssl/openssl.cnf
[ tsa_config1 ]
# These are used by the TSA reply generation only.
dir = ./demoCA # TSA root directory
serial = $dir/tsaserial # The current serial number (mandatory)
crypto_device = builtin # OpenSSL engine to use for signing
signer_cert = $dir/tsacert.pem # The TSA signing certificate
# (optional)
certs = $dir/cacert.pem # Certificate chain to include in reply
# (optional)
signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
default_policy = tsa_policy1 # Policy if request did not specify it
# (optional)
other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
digests = md5, sha1 # Acceptable message digests (mandatory)
accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
clock_precision_digits = 0 # number of digits after dot. (optional)
ordering = yes # Is ordering defined for timestamps?
# (optional, default: no)
tsa_name = yes # Must the TSA name be included in the reply?
# (optional, default: no)
ess_cert_id_chain = no # Must the ESS cert id chain be included?
# (optional, default: no)
二、搭建CA,创建目录测试目录test
mkdir -p ~/test
cd ~/test
cp /usr/local/etc/openssl/openssl.cnf .
三、根据配置文件,创建对应目录
mkdir ./demoCA ./demoCA/newcerts ./demoCA/private
chmod 777 ./demoCA/private
echo '01' > ./demoCA/serial
touch ./demoCA/index.txt
四、修改配置文件
dir = ./demoCA # Where everything is kept
将路径改为自己测试目录路径
dir = ~/test/demoCA # Whereeverything is kept
五、具体操作步骤
1、生成私钥:server.key
openssl genrsa -des3 -out server.key 4096
2、生成证书签名申请文件:server.csr
openssl req -new -key server.key -out server.csr -config openssl.cnf
3、有申请文件server.csr,一般会将server.csr文件提供给第三方可信任机构签名。这里将自建CA(root),用CA的root证书来签名,其中CA的证书保存为root.crt,key保存为root.key。
openssl req -new -x509 -keyout root.key -out root.crt -config openssl.cnf
4、有了自建CA(root),就可以用它来为第2步生的csr文件进行签名,生成server.crt。
openssl ca -in server.csr -out server.crt -cert root.crt -keyfile root.key -config openssl.cnf
5、证书吊销列表CRL,现在来生成server.crl文件,即将刚才的证书吊销。
a、先根据配置文件配置环境,如下:
cp root.key ./demoCA/private/cakey.pem
cp root.crt ./demoCA/cacert.pem
echo '00' > ./demoCA/crlnumber
b、吊销证书
openssl ca -revoke server.crt -config openssl.cnf
c、生成吊销列表
openssl ca -gencrl -out server.crl -config openssl.cnf
六、最后所有生成的文件如下:
test tree
.
├── demoCA
│ ├── cacert.pem
│ ├── crlnumber
│ ├── crlnumber.old
│ ├── index.txt
│ ├── index.txt.attr
│ ├── index.txt.attr.old
│ ├── index.txt.old
│ ├── newcerts
│ │ └── 01.pem
│ ├── private
│ │ └── cakey.pem
│ ├── serial
│ └── serial.old
├── openssl.cnf
├── root.crt
├── root.key
├── server.crl
├── server.crt
├── server.csr
└── server.key