四、 证书操作
在CryptoAPI里,一般使用CERT_CONTEXT类型,即证书上下文,对数字证书进行操作,定义如下。
typedef struct _CERT_CONTEXT {
DWORD dwCertEncodingType;
BYTE *pbCertEncoded;
DWORD cbCertEncoded;
PCERT_INFO pCertInfo;
HCERTSTORE hCertStore;
} CERT_CONTEXT, *PCERT_CONTEXT
在这其中dwCertEncodingType是证书编码类型,常用值为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;pbCertEncoded为证书数据;cbCertEncoded是数据长度字节数;pCertInfo是证书的各项属性信息,CERT_INFO的结构就不再贴了,大家可以自行查阅介绍,很容易懂;hCertStore是证书所在的证书库。
获得证书上下文的方法很多,下面介绍两种:
- 从证书库里获得
1.HCERTSTORE CertOpenStore(
LPCSTR lpszStoreProvider,
DWORD dwEncodingType,
HCRYPTPROV_LEGACY hCryptProv,
DWORD dwFlags,
const void *pvPara
)
通过CertOpenStore方法打开一个证书库。lpszStoreProvider是证书库类型,最常用的几种设置值说明如下表:
值 | 说明 |
---|---|
CERT_STORE_PROV_FILE | 从一个本地文件打开证书库,这个文件应该是一个序列化的证书库数据 |
CERT_STORE_PROV_MEMORY | 从缓存打开一个空白证书库。 |
CERT_STORE_PROV_PHYSICAL | 从指定的计算机上打开证书库。 |
CERT_STORE_PROV_PKCS7 | 从PKCS7格式的签名信息里打开证书库。 |
CERT_STORE_PROV_REG | 从指定的注册表位置打开证书库。 |
CERT_STORE_PROV_SERIALIZED | 根据一个序列化的证书库数据打开证书库。 |
CERT_STORE_PROV_SYSTEM | 打开指定的证书库,这个证书库可以来自多台计算机(最常用)。 |
CERT_STORE_PROV_SYSTEM_REGISTRY | 从一个注册的计算机打开指定的证书库(在本地计算机使用情况下与CERT_STORE_PROV_SYSTEM相同)。 |
dwEncodingType没有实际用处,设置为0;hCryptProv指向一个CSP句柄,推荐传NULL,即使用默认CSP即可;dwFlags的低2字节部分定义了打开证书库的行为特性,如下表:
值 | 说明 |
---|---|
CERT_STORE_CREATE_NEW_FLAG | 新建一个符合条件的证书库,如果这个证书库已存在就会失败。 |
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | 关闭证书库时会检查从库里返回的证书是否都被释放,只有全部释放了才会真正关闭证书库。 |
CERT_STORE_DELETE_FLAG | 删除要打开的证书库。 |
CERT_STORE_ENUM_ARCHIVED_FLAG | 把标记为存档(ARCHIVED)的证书也遍历处理。 |
CERT_STORE_MANIFOLD_FLAG | 将旧版本的证书存档。 |
CERT_STORE_NO_CRYPT_RELEASE_FLAG | 证书库关闭时不释放hCryptProv指定的CSP句柄。hCryptProv不为NULL时此设置有效。 |
CERT_STORE_OPEN_EXISTING_FLAG | 打开已经存在的证书库,如果证书库不存在,则失败。 |
CERT_STORE_READONLY_FLAG | 以只读方式打开证书库。 |
CERT_STORE_SET_LOCALIZED_NAME_FLAG | 为证书库设置本地名称。这个设置需要CSP支持,而且lpszStoreProvider是CERT_STORE_PROV_FILENAME,CERT_STORE_PROV_SYSTEM,CERT_STORE_PROV_SYSTEM_REGISTRY和CERT_STORE_PROV_PHYSICAL才有效。 |
CERT_STORE_UPDATE_KEYID_FLAG | 证书库里每个key的CERT_KEY_PROV_INFO_PROP_ID属性自动更新为key的标识值。 |
如果lpszStoreProvider是CERT_STORE_PROV_SYSTEM、CERT_STORE_PROV_SYSTEM_REGISTER或CERT_STORE_PROV_PHYSICAL,dwFlags的高2字节部分用于标识证书库所在位置。比如:
当前用户:CERT_SYSTEM_STORE_CURRENT_USER
本地计算机:CERT_SYSTEM_STORE_LOCAL_MACHINE
dwFlags的其他设置可以参考CryptoAPI手册。
CertOpenStore如果调用成功,则返回打开的证书库的句柄,否则返回NULL。
pvPara指向的数据和lpszStoreProvider有关,具体说明情况下表:
lpszStoreProvider | pvPara |
---|---|
CERT_STORE_PROV_FILE | 文件句柄 |
CERT_STORE_PROV_MEMORY | 不需要设置 |
CERT_STORE_PROV_PHYSICAL | 证书库名+“\”+计算机名,比如Root.LocalMachine |
CERT_STORE_PROV_PKCS7 | PKCS7格式的签名数据。 |
CERT_STORE_PROV_REG | 注册表键句柄。 |
CERT_STORE_PROV_SERIALIZED | 序列化的证书库数据。 |
CERT_STORE_PROV_SYSTEM | Unicode或ASCII编码的证书库名称,比如MY或Root。 |
CERT_STORE_PROV_SYSTEM_REGISTRY | Unicode或ASCII编码的证书库名称,比如MY或Root。 |
2.PCCERT_CONTEXT CertEnumCertificatesInStore(
HCERTSTORE hCertStore,
PCCERT_CONTEXT pPrevCertContext
)
调用此方法将返回证书库里的证书上下文。hCertStore是证书库句柄,由CertOpenStore返回;pPrevCertContext是前一个证书的证书上下文对象,如果传NULL,表示返回第一个证书上下文对象,否则返回排在pPrevCertContext下一个位置的证书的上下文对象;如果没有证书可以返回,此方法返回NULL。
通过循环调用CertEnumCertificatesInStore,即可实现对指定证书库的遍历。我们可以根据指定的条件,得遍历的结果进行查找过滤,得到需要的证书上下文或集合。
当返回的上下文对象不再使用时,应当调用CertFreeCertificateContext方法释放。
BOOL CertFreeCertificateContext(
PCCERT_CONTEXT pCertContext
)
pCertContext是要释放的上下文对象。
- 根据证书数据直接生成
有时我们已经从其他途径拿到了证书数据,此时就可以通过证书数据直接生成上下文对象。这种情况下,调用CertCreateCertificateContext方法即可。
PCCERT_CONTEXT CertCreateCertificateContext(
DWORD dwCertEncodingType,
const BYTE *pbCertEncoded,
DWORD cbCertEncoded
)
dwCertEncodingType为编码类型,老规矩,一般设置成X509_ASN_ENCODING | PKCS_7_ASN_ENCODING即可;pbCertEncoded是证书数据;cbCertEncoded是证书数据长度。注意,如果我们拿到的证书数据是BASE64格式的,那需要先将数据转换成二进制字节数组。方法调用成功会返回证书上下文对象,失败返回NULL,调用GetLastError返回具体错误信息。
同样,上述方法返回的证书上下文对象使用完毕后,需要调用CertFreeCertificateContext方法释放。