在用openssl读取密钥的时候,从openssl的源码的找到一个很好的示例,完美的示范了读不同格式,不同密钥来源的程序,整理一下,用来学习是相当不错
完整的代码位于…/apps/app.c中
函数是
EVP_PKEY *load_key(BIO *err,const char *file,int format,int maybe_stdin,const char *pass,ENGINE *e,const char *key_descript)
{
const SSL_METHOD *meth;
meth = SSLv23_client_method();
//meth = TLSv1_client_method();
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
//参数1:错误输出 bio,一般就是stderr了
//
BIO *bio_err = NULL;
if(bio_err == NULL)
bio_err = BIO_new_fp(stderr,NIO_MOCLOSE);
//参数2 : 密钥文件名字,可以是pem,der,pfx,或者engine中key的label
char *key_file = NULL;
//参数3 : 密钥格式
int key_format = FORMAT_PEM; //openssl中默认一般都是 PEM格式
//FORMAT_TEXT
//FORMAT_ASN1
//FORMAT_PKCS12
//FORMAT_PEMRSA
//FORMAT_ASN1RSA
//FORMAT_ENGINE
//参数4:密钥来自非文件(stdin)
int key_stdin = 0;
//参数5:密钥的密码
char *passarg = NULL; //参数,可能来自文件,也可能直接是密码
// pass:
// env:
// file:
// fd:
// stdin:
char *pass = NULL; //解析出来的密码字串
//参数6:engine
char *engine_id = NULL; //engine的名字
ENGINE *e = NULL;
e = ENGINE_by_id(engine_id);
//e = try_load_engine(bio_err,engine_id,debug);
ENGINE_set_default(e,ENGINE_METHOD_ALL);
//参数7:密钥的描述
const char *key_descript = "client certificate private key file";
//EVP_PKEY *load_key(BIO *err,const char *file,int format,int maybe_stdin,
// const char *pass,ENGINE *e,const char *key_descript)
//好,现在来读密钥
EVP_PKEY *key = NULL;
key = load_key(bio_err,key_file,key_format,key_stdin,pass,e,key_descript);
/****************************************************************/
//开始分析过程
{
PW_CB_DATA cb_data;
cb_data.password = pass;
cb_data.prompt_info = file;
BIO *key = NULL;
EVP_PKEY *pkey = NULL;
//key必须来自file,stdin,engine中的一种
if(engine) //engine
{
pkey = ENGINE_load_private_key(e,key_file,ui_method,&cb_data);
goto end;
}
key=BIO_new(BIO_s_file());
if(key_stdin) //stdin
{
BIO_set_fp(key,stdin,BIO_NOCLOSE);
}
else(key_file) //file
{
BIO_read_filename(key,key_file)
}
//格式转换
if(format == FORMAT_ASN1) //der
{
pkey = d2i_PrivateKey_bio(key,NULL);
}
else if(format == FORMAT_PEM) //pem
{
pkey = PEM_read_bio_PrivateKey(key,NULL,
(pem_password_cb *)password_callback,&cb_data);
}
else if(format == FORMAT_PKCS12) //p12
{
//这个过程比较多,还是参考源码,我只给出流程和常用函数
//load_pkcs12()
{
PKCS12 *p12;
char *pass;
p12 = d2i_PKCS12_bio(key,NULL);
if(PKCS12_verify_mac(p12,"",0) || PKCS12_verify_mac(p12,NULL,0))
{
pass = "";
}else
{
PKCS12_verify_mac(p12,tpass,len)
}
//最后,PKCS12可以从p12文件中解析出pkey,cert,ca
ret = PKCS12_parse(p12,pass,pkey,cert,ca);
//释放
if(p12)
PKCS12_free(p12);
}
}else
{
//其他格式的解析,源码里都有,不常用就不列出来了
}
return pkey;
}
//获取密钥后将密钥填充到ssl 的 ctx中
SSL_CTX *ctx = NULL;
ctx = SSL_CTX_new(meth);
SSL_CTX_use_PrivateKey(ctx,key);
//在填充了证书之后往往还会检查证书公钥与私钥是否匹配
SSL_CTX_check_private_key(ctx);
SSL *con = NULL;
con = SSL_new(ctx);
}
说明:
1.在这段代码中出现的部分结构不存在openssl的标准头文件中,而是在app.h或者其他文件中
2.当然,openssl也提供了如
SSL_CTX_use_PrivateKey_file(ctx,"client.key",SSL_FILETYPE_PEM);
//SSL_FILETYPE_PEM
//SSL_FILETYPE_ASN1
这样的调用,但是好像只支持 x509的证书
//也有设置密码的接口
SSL_CTX_set_default_passwd_cb_userdata()