今天晚上打完球真累,没心思学习了,所以把这一阵学的OpenSSL的CRL大概总结一下。
CRL(Certificate Revocation List),证书撤销列表,是在证书撤销时用的(好像是废话,晕)。当证书因为一些原因(比如,证书到期,证书私钥丢失等)会被CA吊销,CA为了让别人 知道某个证书被吊销了,会定期发布一个CRL,CRL包含了在这个CRL发布时,被吊销的证书。客户端拿到这个CRL后,就可以知道哪些证书已经无效了。 不过,CRL的发布是有一定的周期的,所以通过CRL的方法,客户端不能实时地检测某个证书是否有效。为了弥补CRL这个不足,现在一般都用 OCSP(Online Certificate Status Protocol),在线证书状态协议。
CRL在的官方文档可以在RFC5280中看到。
CRL在OpenSSL中的表示是通过下面几个结构体完成的,结构体定义在文件x509.h中:
typedef struct X509_revoked_st
{ //这个结构代表了一个被吊销的证书
ASN1_INTEGER *serialNumber; //证书序列号
ASN1_TIME *revocationDate; //被吊销的日期
STACK_OF(X509_EXTENSION) /* optional */ *extensions;
int sequence; /* load sequence */
} X509_REVOKED;
typedef struct X509_crl_info_st
{ //CRL的信息
ASN1_INTEGER *version; //CRL的版本号
X509_ALGOR *sig_alg; //CRL所使用的签名算法
X509_NAME *issuer; //CRL的发布者
ASN1_TIME *lastUpdate; //CRL发布的日期
ASN1_TIME *nextUpdate; //下一次发布CRL的日期
STACK_OF(X509_REVOKED) *revoked; //被吊销的证书的序列
STACK_OF(X509_EXTENSION) /* [0] */ *extensions;
ASN1_ENCODING enc;
} X509_CRL_INFO;
struct X509_crl_st
{ //CRL结构体
/* actual signature */
X509_CRL_INFO *crl;
X509_ALGOR *sig_alg; //CRL所使用的签名算法,和结构X509_crl_info_st中sig_alg的值一样
ASN1_BIT_STRING *signature; //CRL的签名
int references;
} /* X509_CRL */ ;
在文件ossl_typ.h中有:
typedef struct X509_crl_st X509_CRL;
我们下面要操作的CRL就是X509_CRL。
1.生成一个空的CRL
调用函数X509_CRL_new()
eg. X509_CRL *crl = X509_CRL_new();
2.设置CRL中各信息的值
调用函数X509_CRL_set_XXX ,其中XXX可以是version, issuer, lastUpdate, nextUpdate。
3.给CRL签名
调用X509_CRL_sign()
eg. X509_CRL_sign(crl, pkey, EVP_md5());
其中,pkey是一个EVP_PKEY型的指针变量。
4.给CRL中添加一个扩展项
调用函数int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit, unsigned long flags)
这个函数自动为用户生成一个X509_EXTENSION结构体,定义如下:
typedef struct X509_extension_st
{
ASN1_OBJECT *object;
ASN1_BOOLEAN critical;
ASN1_OCTET_STRING *value;
} X509_EXTENSION;
参数x代表要操作的CRL,nid, value, crit分别是X509_EXTENSION中的object, value, critical,flag可以取下面的值:
#define X509V3_ADD_OP_MASK 0xfL
#define X509V3_ADD_DEFAULT 0L
#define X509V3_ADD_APPEND 1L
#define X509V3_ADD_REPLACE 2L
#define X509V3_ADD_REPLACE_EXISTING 3L
#define X509V3_ADD_KEEP_EXISTING 4L
#define X509V3_ADD_DELETE 5L
#define X509V3_ADD_SILENT 0x10
一般用X509V3_ADD_DEFAULT就行了,也就是直接传0就OK了。
需要注意的是,函数X509_CRL_add1_ext_i2d要求传入的value是一个ASN1_OCTET_STRING*型的。开始以为,是void*就可以传任何类型的指针,结果在函数X509_CRL_add1_ext_i2d运行时,会产生段错误。
函数X509_CRL_add1_ext_i2d调用的主要的函数如下图:
int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit, unsigned long flags)
|(在文件x509_ext.c中)
|此函数的作用是,把nid, value, crit组合成一个X509_EXTENSION结构,
|通过flags指示的动作加入到x中。
int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, int crit, unsigned long flags)
| (在文件v3_lib.c中)
| 此函数的作用是,通过调用X509V3_EXT_i2d把nid, value, crit组合成一个X509_EXTENSION结构,
| 通过flags指示的动作加入到x中。
X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc)
| (在文件v3_conf.c中)
| 此函数的作用是,把传入的三个参数组合成一个X509_EXTENSION结构,
| 这里的三个参数分别就是nid, crit, value
static X509_EXTENSION *do_ext_i2d(X509V3_EXT_METHOD *method, int ext_nid, int crit, void *ext_struc)
| (在文件v3_conf.c中)
| 这是一个内部函数,作用是通过method中的函数,把ext_struc转换成ASN1_OCTET_STRING,
| 然后调用X509_EXTENSION_create_by_NID生成一个X509_EXTENSION
X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, int nid, int crit, ASN1_OCTET_STRING *data)
| (在文件x509_v3.c中)
| 此函数的作用是,把nid转换成ASN1_OBJECT,
| 并通过调用X509_EXTENSION_create_by_OBJ生成一个X509_EXTENSION
X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **ex, ASN1_OBJECT *obj, int crit, ASN1_OCTET_STRING *data)
(在文件x509_v3.c中)
此函数的作用是,通过调用X509_EXTENSION_new, X509_EXTENSION_set_object,
X509_EXTENSION_set_critical, X509_EXTENSION_set_data生成一个X509_EXTENSION
5.将CRL存入PEM格式文件
调用函数PEM_write_X509_CRL()
eg. PEM_write_X509_CRL(fp, crl);
这个函数是把CRL以PEM格式存入文件的。
6.从PEM格式文件中读取CRL
调用函数PEM_read_X509_CRL()
eg. PEM_read_X509_CRL(fp, crl, NULL, NULL);
其中,crl是一个指向X509_CRL的指针的指针。