1.1 What to do
通过Openssl和PKCS#11接口,使用USBKEY中的私钥和证书来签发一个下级证书。
1.2 背景
数字证书颁发过程一般为:用户首先产生自己的密钥对,并将公共密钥及部分个人身份信息传送给认证中心。认证中心在核实身份后,将执行一些必要的步 骤,以确信请求确实由用户发送而来,然后,认证中心将发给用户一个数字证书,该证书内包含用户的个人信息和他的公钥信息,同时还附有认证中心的签名信息。
一个标准的X.509数字证书包含以下一些内容:
证书的版本信息;
证书的序列号,每个证书都有一个唯一的证书序列号;
证书所使用的签名算法;
证书的发行机构名称,命名规则一般采用X.500格式;
证书的有效期,现在通用的证书一般采用UTC时间格式,它的计时范围为1950-2049;
证书所有人的名称,命名规则一般采用X.500格式;
证书所有人的公开密钥;
证书发行者对证书的签名。
简而言之,CA从PKCS#10证书请求(或者P7格式)中读取用户信息和公钥信息,使用这些信息封装成一个X.509格式(可能是不同版本,比较 普遍是V3),此时唯一没有包括的是证书发行者对证书的签名,此时使用CA的私钥进行签名,得到签名值后CA将其填充到X.509相对应的结构中去,一个 X.509证书宝宝就此诞生了。
此处唯一不同的是CA的公私钥对和证书都存放在USBKEY中(当然也能存放在加密机或加密卡中),所以将通过USBKEY的PKCS#11接口完成上述操作,而证书相关操作就由Openssl代劳了。
1.3 正题
第一、使用Usbkey向某个CA申请一个证书
通过下面的命令来验证,第一组公私钥对和证书是签名证书,第二组是加密证书。可以很明显地看出他们是通过Csp方式操作整个证书申请过程的。
C:\Program Files\Smart card bundle>pkcs11-tool.exe --module DMPKCS11.dll –O
Certificate Object, type = X.509 cert
label: cert addey by CSP
ID: 37af001ddbd525e640ca3c3f6d78b009741d1f48
Public Key Object; RSA 1024 bits
label: pub key addey by CSP
ID: 37af001ddbd525e640ca3c3f6d78b009741d1f48
Usage: encrypt, verify
Private Key Object; RSA
label: private key addey by CSP
ID: 37af001ddbd525e640ca3c3f6d78b009741d1f48
Usage: decrypt, sign
Certificate Object, type = X.509 cert
label: cert addey by CSP
ID: ab268f4320a426b4a6ce70d757cd11fcd83b8ddd
Public Key Object; RSA 1024 bits
label: pub key addey by CSP
ID: ab268f4320a426b4a6ce70d757cd11fcd83b8ddd
Usage: encrypt, verify
Private Key Object; RSA
label: private key addey by CSP
ID: ab268f4320a426b4a6ce70d757cd11fcd83b8ddd
Usage: decrypt, sign
第二、生成PKCS#11的证书请求
这里直接使用Java程序生成一个证书请求。
import java.io.OutputStreamWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.openssl.PEMWriter;
/**
* Generation of a basic PKCS #10 request.
*/
public class PKCS10CertRequestExample
{
public static PKCS10CertificationRequest generateRequest(
KeyPair pair)
throws Exception
{
return new PKCS10CertificationRequest(
"SHA256withRSA",
new X500Principal("C=CN,ST=上海,L=上海,O=火星,OU=北极,CN=超人"),
pair.getPublic(),
null,
pair.getPrivate());
}
public static void main(
String[] args)
throws Exception
{
// create the keys
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
kpGen.initialize(1024, Utils.createFixedRandom());
KeyPair pair = kpGen.generateKeyPair();
PKCS10CertificationRequest request = generateRequest(pair);
PEMWriter pemWrt = new PEMWriter(new OutputStreamWriter(System.out));
pemWrt.writeObject(request);
pemWrt.close();
}
}
证书请求
-----BEGIN CERTIFICATE REQUEST-----
MIIBoDCCAQkCAQAwYjEPMA0GA1UEAwwG6LaF5Lq6MQ8wDQYDVQQLDAbljJfmnoEx
DzANBgNVBAoMBueBq+aYnzEPMA0GA1UEBwwG5LiK5rW3MQ8wDQYDVQQIDAbkuIrm
tbcxCzAJBgNVBAYTAkNOMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw7iyU
/8p1lCxnJifdqxNYO1cTVg35BBtscQsrtug9Br3Vge/kNX9KC5xOGhdcK1IDjl3d
1CGsRtnb4dEFqtkjKWQ1z5WZxXWoVfkqwP3AJg8y10BhiiDqPPbn3II4o8Nc+bvz
tDm32HbNXcyXWLR5aEJx1FiJYdDmDbRbgGrcawIDAQABMA0GCSqGSIb3DQEBCwUA
A4GBAJSr2pe1LJp++gSWAc7yVufbnYXG3QgzIdoEUhP1I/3LNeqUYyuTaL/fTgAF
oEjTvwOlAVizcve8qiD9/ApY+MtjgRKFDbZYnkC3mRgJTDxV3WzDmdj4YEQGIUVG
O+XRfiWP132n9N3aI6gaJVj2m7Zu56akrE3F2c4kawZL/aIK
-----END CERTIFICATE REQUEST-----
第三、程序签发
1. engine_pkcs11的使用方式
使用openssl调用USBKEY的PKCS#11接口,可以通过OpenSC项目的engine_pkcs11接口。原本使用编写openssl配置 文件方式(见[1]),但是就是无法使用,两次调用ListEngine()都无法发现pkcs11 engine的影子。
Openssl.conf 内容:
openssl_conf = openssl_def
[openssl_def]
engines = engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = "C:\\Program Files\\Smart card bundle\\engine_pkcs11.dll"
MODULE_PATH = C:\\Windows\\System32\\DMPKCS11.dll
init = 0
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
可以通过下面命令验证配置文件并没有写错,openssl识别出了pkcs11 engine,并且生成了证书请求。
C:\Program Files\Smart card bundle>openssl req -config openssl.conf -engine pkcs11 -new -key id_37af001ddbd525e640ca3c3f6d78b009741d1f48 -keyform engine -out req.pem -text -x509 -subj "/CN=Andreas Jellinghaus"
engine "pkcs11" set.
PKCS#11 token PIN:
所以最后还是使用动态调用的方式导入pkcs11 engine,即ENGINE_load_dynamic。所以两次调用ListEngine()后发现, dynamic engine导入pkcs11 engine后就会被其替换。
导入前
id: dynamic, name: Dynamic engine loading support
导入后
id: pkcs11, name: pkcs11 engine
2. 导出USBKEY中的CA证书
需要导出CA证书,这是因为CA需要填充X.509格式中的证书的发行机构名称。
通过” LOAD_CERT_CTRL”命令来获取证书,输入的参数为证书的表示。
"slot_0-id_37af001ddbd525e640ca3c3f6d78b009741d1f48"
slot_0 PKCS#11 表示的第一个插槽(一个插槽配一个Token)
id_37af001ddbd525e640ca3c3f6d78b009741d1f48 证书的Id号(同一组公私钥对和证书这个ID是相同的),这个ID可以通过pkcs11-tools获得。
命令返回的parms.cert就指向一个X.509结构的证书。
但是必须要注意的是导出证书前,必须设置过正确的PIN
struct {
const char * cert_id;
X509 * cert;
} parms;
parms.cert_id = "slot_0-id_37af001ddbd525e640ca3c3f6d78b009741d1f48";
parms.cert = NULL;
ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1);
3. 证书请求
通过JAVA生成的证书请求,直接复制粘贴到工程目录下的文本文件certreq.txt即可,并且需要包含BEGIN/END部分。
4. 从证书请求中获取用户信息
//设置证书的主体名称,req就是刚刚生成的请求证书
X509_set_subject_name(m_pClientCert, X509_REQ_get_subject_name(req));
//设置证书的公钥信息
X509_set_pubkey(m_pClientCert, X509_PUBKEY_get(req->req_info->pubkey));
5. 设置证书的签发者信息
//设置证书的签发者信息,m_pCACert是CA证书
X509_set_issuer_name(m_pClientCert, X509_get_subject_name(m_pCACert));
6. 证书签名
注意这里采用的是sha1的摘要算法,当然也可使用MD5
//设置签名值
// EVP_sha1 是否可以设置成别的,如EVP_md5
// 这样一份X509证书就生成了,下面的任务就是对它进行编码保存。
X509_sign(m_pClientCert, m_pCAKey, EVP_sha1());
此处还有些补充的内容,为了验证X509_sign调用PKCS#11接口的情况,自己实现了一个PKCS#11的包装壳(68个导出函数),实现 时注意C_GetFunctionList应该指向本包装壳的函数,不然错误的使用实际的C_GetFunctionList作返回结构便也就失去意义 了。X509_sign的调用方式还是不同的,java中如果使用SHA1WithRSA传入到PKCS#11接口的C_Sign或者 C_SignUpdate的数据是完整的明文,但是X509_sign传入的是一个ASN.1 Sequence的一个结构,结构中包含待签名数据的摘要散列。
举例来说:
待加密的数据是Hello World! ,在C_Sign传入的数据中就可以发现Hello World!的SHA-1的摘要散列。
待加密:Hello World!
SHA-1: 2EF7BDE608CE5404E97D5F042F95F89F1C232871
C_Sign:
30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 2e f7 bd e6 08 ce 54 04 e9 7d 5f 04 2f 95 f8 9f 1c 23 28 71
使用ASN.1dump来观察就看的更加清楚了。
从X509_sign的实现也可以看的这一点,在RSA_Sign之前首先进行摘要算法,并且这个摘要并不使用PKCS#11中的接口函数,直接使用Openssl自己的摘要算法,所以传入到最后的只是明文摘要散列了。
int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
{
//先进行ret->cert_info->signature,以及ret->sig_alg的设置;
inl=i2d_X509_CINF(ret->cert_info,NULL);//求出证书编码后的长度
buf_in=(unsigned char *)OPENSSL_malloc((unsigned int)inl);//申请空间
outll=outl=EVP_PKEY_size(pkey1);
buf_outl=(unsigned char *)OPENSSL_malloc((unsigned int)inl);
if ((buf_in == NULL) ││ (buf_outl== NULL))
{
outl=0;
goto err;
}
p=buf_in;//p与buf-in共享一段地址
i2d_X509_CINF(ret->cert_info,&p);//将证书编码存入buf-in
EVP_MD_CTX_init(&ctxl);//初始化
EVP_SignInit(&ctxl,dgst);//将需要使用的摘要算法存入ctxl中
EVP_SignUpdate(&ctxl,(unsigned char *)buf_in,inl);//存入证书的编码值
EVP_DigestFinal(&ctxl,&(m[0]),&m_len);//求取编码的长度为m_len摘要值存入m中
RSA_sign(ctxl->digest->type,m,m_len,buf_out,outl,pkey->pkey.rsa)//求取摘要值的签名值,最后将长度为outl的签名值存入buf-out。
......
7. 最后生成的证书
USBKEY 中包含证书是向三级CA申请的,所以处于第四级,使用第四级证书来签发新证书,” 超人”宝宝就只能到第五级去了(也许是第五项修炼吧)。
其实还是个问题,第四级证书报“此证书似乎对于所选的目的是有效。”,出现此问题的原因嵌入在消息中的签名证书链包含一个无效的交叉引用,估计第四级是一个用户证书,要消除这个感叹号,第四级证书的证书用法中应该包含Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86)这几项
8.完整代码
// SignWithOpenSSL.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/engine.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/rsa.h>
#include <openssl/x509v3.h>
#define OPENSSL_LOAD_CONF
#define UC_ENGINE_SOPATH "C:\\Program Files\\Smart card bundle\\engine_pkcs11.dll"
#define UC_EXPECTED_ENGINE_ID "pkcs11"
#define UC_ENGINE_MODULEPATH "C:\\Windows\\System32\\DMPKCS11.dll"
// 列出当前所有的engine
/*
ENGINE *ENGINE_get_first(void);
ENGINE *ENGINE_get_last(void);
ENGINE *ENGINE_get_next(ENGINE *e);
ENGINE *ENGINE_get_prev(ENGINE *e);
*/
void ListEngine()
{
ENGINE *current;
current = ENGINE_get_first();
if( NULL != current )
{
printf("id: %s, name: %s\n",
ENGINE_get_id(current),
ENGINE_get_name(current));
while( NULL != (current = ENGINE_get_next(current)))
{
printf("id: %s, name: %s\n",
ENGINE_get_id(current),
ENGINE_get_name(current));
}
}
}
/*存储证书*/
int save_cert(X509 *pCert, char *pCertFile)
{
BIO *pbio;
if(NULL == pCert || NULL == pCertFile)
{
return -1;
}
pbio = BIO_new_file(pCertFile, "w");
if(NULL == pbio)
{
return -1;
}
if(!i2d_X509_bio(pbio, pCert))
{
printf("save_cert:call PEM_write_bio_X509 error ");
return -1;
}
printf("Bingo, New Cert is borned\n");
BIO_free(pbio);
return 0;
}
void add_subject_entity(X509_NAME *pSubjectName, char *key, char *value)
{
int nid;
X509_NAME_ENTRY *ent;
if( (nid =OBJ_txt2nid(key)) == NID_undef )
{
printf(" add_subject_entity:concert nid error");
return ;
}
ent = X509_NAME_ENTRY_create_by_NID( NULL, nid, MBSTRING_UTF8,
(unsigned char*)value, -1);
if(ent == NULL)
{
printf("add_subject_entity:create ent error");
return;
}
if(X509_NAME_add_entry(pSubjectName, ent, -1, 0) != 1)
{
printf("add_subject_entity:add to subjectname error");
return;
}
return;
}
int CreateX509Cert(X509 *m_pCACert, EVP_PKEY *m_pCAKey)
{
// 读取证书请求
BIO *in;
X509_REQ *req=NULL,**req2=NULL;
in = BIO_new_file("certreq.txt","r");
req = PEM_read_bio_X509_REQ(in,NULL,NULL,NULL);
if( req == NULL )
{
printf("DER Decode Error!\n");
}
else
{
printf("DER Decode Success!\n");
}
// 使用usbkey中的私钥进行签名
X509 *m_pClientCert;
m_pClientCert = X509_new();
//设置版本号
X509_set_version(m_pClientCert, 2);
//设置证书序列号,这个sn就是CA中心颁发的第N份证书
ASN1_INTEGER_set(X509_get_serialNumber(m_pClientCert),100);
//设置证书开始时间
X509_gmtime_adj(X509_get_notBefore(m_pClientCert),0);
//设置证书结束时间
X509_gmtime_adj(X509_get_notAfter(m_pClientCert), (long)60*60*24);
//设置证书的主体名称,req就是刚刚生成的请求证书
X509_set_subject_name(m_pClientCert, X509_REQ_get_subject_name(req));
//设置证书的公钥信息
X509_set_pubkey(m_pClientCert, X509_PUBKEY_get(req->req_info->pubkey));
//设置证书的签发者信息,m_pCACert是CA证书
X509_set_issuer_name(m_pClientCert, X509_get_subject_name(m_pCACert));
//设置扩展项目
X509V3_CTX ctx;
X509V3_set_ctx(&ctx, m_pCACert, m_pClientCert, NULL, NULL, 0);
X509_EXTENSION *x509_ext = X509_EXTENSION_new();
x509_ext = X509V3_EXT_conf(NULL, &ctx, "HELLO", "HELLO");
X509_add_ext(m_pClientCert,x509_ext,-1);
//设置签名值
// EVP_sha1 是否可以设置成别的,如EVP_md5
// 这样一份X509证书就生成了,下面的任务就是对它进行编码保存。
X509_sign(m_pClientCert, m_pCAKey, EVP_sha1());
// 输出证书
save_cert(m_pClientCert, "d:\\test.cer");
return 0;
}
int main(int argc, CHAR* argv[])
{
ENGINE *e;
const char *engine_id = "pkcs11";
const char *key_id = "37af001ddbd525e640ca3c3f6d78b009741d1f48";
UI_METHOD *ui_method = NULL;
EVP_PKEY *priv_key;
void *cb_data;
const char *config_name = NULL;
BIO *bio_err=NULL;
/* Load the config file */
//OPENSSL_config(config_name); // 不使用Openssl0.9.8e的配置文件来导入PKCS11
ENGINE_load_dynamic();
ListEngine();
printf("\nLoading Dynamic...\n");
/* Register engine */
printf("Registering enginen");
e = ENGINE_by_id("dynamic");
if(!e) {
/* the engine isn't available */
printf("The engine isn't available\n");
return 0;
}
//int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void));
ENGINE_ctrl(e, ENGINE_CTRL_SET_LOGSTREAM, 0, bio_err, 0);
// 设置engine_pkcs11的路径
ENGINE_ctrl_cmd_string(e, "SO_PATH", UC_ENGINE_SOPATH, 0);
ENGINE_ctrl_cmd_string(e, "ID", UC_EXPECTED_ENGINE_ID, 0);
ENGINE_ctrl_cmd_string(e, "LIST_ADD", "1", 0);
ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0);
// 设置USBKEY厂商PKCS#11实现的路径
ENGINE_ctrl_cmd_string(e, "MODULE_PATH", UC_ENGINE_MODULEPATH, 0);
// 设置PIN码
if(!ENGINE_ctrl_cmd_string(e, "PIN", "111111", 0)){
printf("Error sending PIN to = engine");
ENGINE_free(e);
return 0;
}
ListEngine();
if(!ENGINE_init(e)) {
/* the engine couldn't initialise, release 'e' */
printf("The engine couldn't initialise\n");
ENGINE_free(e);
return 0;
}
if(!ENGINE_register_RSA(e)){
/* This should only happen when 'e' can't initialise, but the previous
* statement suggests it did. */
printf("This should not happen\n");
abort();
}
// 直接从usb-key中导入证书,但必须初始化后。
struct {
const char * cert_id;
X509 * cert;
} parms;
parms.cert_id = "slot_0-id_37af001ddbd525e640ca3c3f6d78b009741d1f48";
parms.cert = NULL;
ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1);
// Load private key
printf("Loading private key\n");
priv_key = ENGINE_load_private_key(e, key_id, ui_method, &cb_data);
// 产生证书
CreateX509Cert(parms.cert, priv_key);
// Release the functional reference from ENGINE_init()
ENGINE_finish(e);
// Release the structural reference from ENGINE_by_id()
ENGINE_free(e);
return 0;
}
1.4 参考
[1] DNSSEC Signers and OpenSSL
[END]
相关推荐
在生成PKCS#10请求时,通常需要使用特定的库或工具,如OpenSSL,将证书请求的各个部分按照PKCS#10的标准结构组合起来,然后使用私钥进行签名。这个过程确保了请求的完整性和安全性。 在提供的压缩包文件"ParseP10...
PKCS(Public Key Cryptography Standards)是由RSA安全公司提出的公钥加密标准,其中PKCS#1主要定义了RSA公钥和私钥的格式,而PKCS#8则定义了通用的非对称密钥的编码格式,支持多种加密算法,包括RSA。 在微信支付...
越来越多的应用需要我们使用USB接口数字证书进行PKCS#7数字签名。本文分别介绍了使用微软CryptoAPI方式和OpenSSL Engine方式进行数字签名。特别地,提出了OpenSSL Engine简化方式,这种方式更为灵活方便易行。
总结来说,OpenSSL Engine PKCS#11源码是理解和实现OpenSSL与PKCS#11设备交互的重要参考资料。它展示了如何在OpenSSL中创建和配置一个自定义引擎,以支持PKCS#11标准,同时也提供了测试代码以验证引擎的功能和性能。...
OpenSSL 库的 pkcs11 引擎插件允许以半透明的方式访问 PKCS#11 模块。 该项目的 wiki 页面位于 ,包括错误跟踪器和源浏览器。 PKCS#11 PKCS#11 API 是一个抽象 API,用于对加密对象(例如私钥)执行操作,而无需...
从OpenSSL的0.9.7版,Engine机制集成到了OpenSSL的内核中,成为了OpenSSL不可缺少的一部分。 Engine机制目的是为了使OpenSSL能够透明地使用第三方提供的软件加密库或者硬件加密设备进行...支持PKCS#11接口的Engine接口
基于BigInteger类用java封装的PKCS#1 v2.1 全算法实现,模块与规范一一对应。包含 I2OSP OS2IP RSAEP RSADP RSASP1 RSAVP1 RSAES-OAEP RSAES-PKCS1_v1_5 RSASSA-PSS RSASSA-PKCS1-v1_5 以及 MGF SourceAlgrithm等...
本示例说明如何使用OpenSSL( )和engine_pkcs11( )来使QSslSocket使用私钥和HSM的证书( )使用PKCS#11( )。 要测试此示例,您将需要OpenSSL标头和库,binary_pkcs11的二进制版本以及PKCS#11模块(所用设备...
在中国银行的支付和报文验签场景中,PKCS#7算法被广泛应用于保证数据的完整性和机密性。本文将详细介绍如何使用PHP来实现PKCS#7的加解密过程。 首先,PKCS#7的主要特点是它可以处理任意长度的数据,并且支持填充...
OpenSSL 生成 CA 证书 PKCS#12 PEM 格式转换 OpenSSL 是目前最流行的 SSL 密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持 SSL/TLS 协议的实现。 OpenSSL 工具箱中包含了大量实用的命令和选项,...
这里提到的"pkcs1与pkcs8证书互相转换"是指在处理公钥证书时,如何在PKCS#1和PKCS#8格式之间进行转换。这两种格式都是用于存储和交换数字证书的标准,但它们的结构和用途有所不同。 **PKCS#1**,也称为RSA密钥格式...
由于业务需求需要对接java接口,对方接口采用AES/ECB/PKCS5Padding加密技术,还经过,16进制转换,php端采用openssl加解密方式。调试可以访问http://tool.chacuo.net/cryptaes
PKCS#11(也称为CryptoKI或PKCS11)是用于与诸如智能卡和硬件安全模块(HSM)之类的硬件加密设备进行交互的标准接口。 该文件是根据PKCS#11 2.30规范开发的,在我们创建此文件时尚未提供2.40标头,应该足够容易...
利用OpenSSL PKCS7进行数字签名的示例代码,仅供参考
openssl之pkcs7 - squallxun的日志 - 网易博客.png
[b]将传统格式的私钥转换成PKCS#8格式的(java使用的是PKCS#8格式的私钥)[/b] openssl pkcs8 -topk8 -inform PEM -in dsa_private_temp_key.pem -outform PEM -nocrypt -out dsa_private_key.pem c) 生成公钥 ...
PKCS11,全称Public Key Cryptography Standards #11,是RSA安全公司制定的一套标准,用于定义加密硬件(如智能卡、硬件安全模块HSM)与软件应用程序之间的接口。这个标准允许软件应用程序透明地使用加密硬件进行...
openCryptoki版本3.15实现了PKCS#11规范版本3.0。 该软件包包括几个加密令牌:CCA,ICA,TPM,SWToken,ICSF和EP11。 有关openCryptoki的更深入概述,请参阅 要求: IBM ICA-需要libica库版本3.3.0或更高版本...
PKCS(Public Key Cryptography Standards)是由RSA安全公司制定的一系列加密标准,其中PKCS#1和PKCS#8涉及到RSA密钥的存储格式。 PKCS#1主要定义了RSA公钥和私钥的编码格式,它包括两种主要的编码方式:RSA加密...
PKCS#11(也称为CryptoKI或PKCS11)是用于与诸如智能卡和硬件安全模块(HSM)之类的硬件加密设备进行交互的标准接口。 它紧密地包装了库,但是在合理的地方尝试使用看起来像“ node.crypto”的形式。 已通过以下...