目前就职于中金公司。由于涉及在线开户相关,所以整理一下~
本文在前人的基础上更加完善:通过openssl 生成证书请求CSR 符合pkcs10的语法规范
为客户端生成,代码为C语言,可以直接在xcode的中于objective-c混合开发。
主要概念:
OpenSSL:一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。
CSR:证书请求 ,Cerificate Signing Request的英文缩写 (我们最终想要的结果,为base64转码格式)
privateKey : 私钥 (也是王我们想要的,W为base64转码格式,以便备用验证等)
pkcs#10:PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准。pkcs10 本质是证书请求语法标准。PKCS#10定义了证书请求的语法。证书请求包含了一个唯一识别名、公钥和可选的一组属性,它们一起被请求证书的实体签名(证书管理协议中的PKIX证书请求消息就是一个PKCS#10)。
代码文件我已上传到Github: https://github.com/liuchengli/CSR-
直接下载代码运查阅或者直接运行代码看看,效果更好
如果我们想要通过代码方式实现证书请求,就要需要用到openssl的函数。
1、因为openssl库是基于C语言的库,为方便在Xcode中使用,我们需要把它编译为静态库。具体如何编译,可以参考这篇文件:在iOS中如何编译openssl的静态库
2、如果不想这么麻烦,大家可以在这里下载已经编译好的.a文件。感谢这些前辈们帮我们省了不少事。libssl.a支持多架构
3、第二步下载下来的文件目录结构是这样的:
a)include目录下包含一个openssl目录,openssl目录底下是所有头文件
b)libssl.a和libcrypto.a
4、把上面所有文件拖到工程中,注意:在TARGET->您的项目名称->Build Settings 里面搜索:Header Search Path,在里面增加配置:$(PROJECT_DIR)/include/
新建一个C File,命名为:header,Xcode会同时帮我们生成一个header.h的头文件。
在header.h中,我们引用一下头文件,并且声明需要用到的函数.
#include
#include
#include
#include
//**在这里需要声明函数**/
// 传入的信息参数,字符串形式一次传入,不传也可以
X509_NAME *parse_name(char *subject, long chtype, int multirdn);
// 传入信息参数,分内容对应传入
X509_NAME *CreateDN(char *pbEmail, char *pbCN, char *pbOU, char *pbO, char *pbL, char *pbST, char *pbC);
// 获取证书请求CSR和私钥
// Created by 刘成利 on 2017/6/1.
// Copyright © 2017年 刘成利. All rights reserved.
//
#include "pkcs10header.h"
/*
* subject is expected to be in the format /type0=value0/type1=value1/type2=...
* where characters may be escaped by \
*/
X509_NAME *parse_name(char *subject, long chtype, int multirdn)
{
size_t buflen = strlen(subject)+1; /* to copy the types and values into. due to escaping, the copy can only become shorter */
char *buf = OPENSSL_malloc(buflen);
size_t max_ne = buflen / 2 + 1; /* maximum number of name elements */
char **ne_types = OPENSSL_malloc(max_ne * sizeof (char *));
char **ne_values = OPENSSL_malloc(max_ne * sizeof (char *));
int *mval = OPENSSL_malloc (max_ne * sizeof (int));
char *sp = subject, *bp = buf;
int i, ne_num = 0;
X509_NAME *n = NULL;
int nid;
if (!buf || !ne_types || !ne_values || !mval)
{
//BIO_printf(bio_err, "malloc error\n");
goto error;
}
if (*subject != '/')
{
//BIO_printf(bio_err, "Subject does not start with '/'.\n");
goto error;
}
sp++; /* skip leading / */
/* no multivalued RDN by default */
mval[ne_num] = 0;
while (*sp)
{
/* collect type */
ne_types[ne_num] = bp;
while (*sp)
{
if (*sp == '\\') /* is there anything to escape in the type...? */
{
if (*++sp)
*bp++ = *sp++;
else
{
//BIO_printf(bio_err, "escape character at end of string\n");
goto error;
}
}
else if (*sp == '=')
{
sp++;
*bp++ = '\0';
break;
}
else
*bp++ = *sp++;
}
if (!*sp)
{
//BIO_printf(bio_err, "end of string encountered while processing type of subject name element #%d\n", ne_num);
goto error;
}
ne_values[ne_num] = bp;
while (*sp)
{
if (*sp == '\\')
{
if (*++sp)
*bp++ = *sp++;
else
{
//BIO_printf(bio_err, "escape character at end of string\n");
goto error;
}
}
else if (*sp == '/')
{
sp++;
/* no multivalued RDN by default */
mval[ne_num+1] = 0;
break;
}
else if (*sp == '+' && multirdn)
{
/* a not escaped + signals a mutlivalued RDN */
sp++;
mval[ne_num+1] = -1;
break;
}
else
*bp++ = *sp++;
}
*bp++ = '\0';
ne_num++;
}
if (!(n = X509_NAME_new()))
goto error;
for (i = 0; i < ne_num; i++)
{
if ((nid=OBJ_txt2nid(ne_types[i])) == NID_undef)
{
//BIO_printf(bio_err, "Subject Attribute %s has no known NID, skipped\n", ne_types[i]);
continue;
}
if (!*ne_values[i])
{
//BIO_printf(bio_err, "No value provided for Subject Attribute %s, skipped\n", ne_types[i]);
continue;
}
if (!X509_NAME_add_entry_by_NID(n, nid, chtype, (unsigned char*)ne_values[i], -1,-1,mval[i]))
goto error;
}
OPENSSL_free(ne_values);
OPENSSL_free(ne_types);
OPENSSL_free(buf);
OPENSSL_free(mval);
return n;
error:
X509_NAME_free(n);
if (ne_values)
OPENSSL_free(ne_values);
if (ne_types)
OPENSSL_free(ne_types);
if (mval)
OPENSSL_free(mval);
if (buf)
OPENSSL_free(buf);
return NULL;
}
X509_NAME *CreateDN(char *pbEmail, char *pbCN, char *pbOU, char *pbO, char *pbL, char *pbST, char *pbC)
{
X509_NAME *pX509Name = NULL;
if(pbCN == NULL)
{
return NULL;
}
if (!(pX509Name = X509_NAME_new()))
{
return NULL;
}
X509_NAME_add_entry_by_txt(pX509Name, "emailAddress", V_ASN1_UTF8STRING, pbEmail, -1, -1, 0);
X509_NAME_add_entry_by_txt(pX509Name, "CN", V_ASN1_UTF8STRING, pbCN, -1, -1, 0);
X509_NAME_add_entry_by_txt(pX509Name, "OU", V_ASN1_UTF8STRING, pbOU, -1, -1, 0);
X509_NAME_add_entry_by_txt(pX509Name, "O", V_ASN1_UTF8STRING, pbO, -1, -1, 0);
X509_NAME_add_entry_by_txt(pX509Name, "L", V_ASN1_UTF8STRING, pbL, -1, -1, 0);
X509_NAME_add_entry_by_txt(pX509Name, "ST", V_ASN1_UTF8STRING, pbST, -1, -1, 0);
X509_NAME_add_entry_by_txt(pX509Name, "C", V_ASN1_UTF8STRING, pbC, -1, -1, 0);
return pX509Name;
}
long int GenCSR(char *pbDN, int nDNLen, char *pCSR, size_t nCSRSize, char *privateKey)
{
char szAltName[] = "DNS:www.cicc.com";
char szComment[] = "Create by liuchengli";
char szKeyUsage[] = "digitalSignature, nonRepudiation";
char szExKeyUsage[] = "serverAuth, clientAuth";
X509_REQ *pX509Req = NULL;
int iRV = 0;
long lVer = 3;
X509_NAME *pX509DN = NULL;
EVP_PKEY *pEVPKey = NULL;
RSA *pRSA = NULL;
X509_NAME_ENTRY *pX509Entry = NULL;
char szBuf[255] = {0};
unsigned char mdout[20];
unsigned int nLen, nModLen;
int bits = 2048;
unsigned long E = RSA_3;
unsigned char *pDer = NULL;
unsigned char *p = NULL;
FILE *fp = NULL;
const EVP_MD *md = NULL;
X509 *pX509 = NULL;
BIO *pBIO = NULL;
BIO *pPemBIO = NULL;
BUF_MEM *pBMem = NULL;
//STACK_OF(X509_EXTENSION) *pX509Ext;
if(pbDN == NULL)
{
return -1;
}
pX509DN = parse_name(pbDN, V_ASN1_UTF8STRING, 0);
pX509Req = X509_REQ_new();
iRV = X509_REQ_set_version(pX509Req, lVer);
iRV = X509_REQ_set_subject_name(pX509Req, pX509DN);
// 签名方式为哈希(非MD5)
md = EVP_sha1();
iRV = X509_REQ_digest(pX509Req, md, mdout, &nModLen);
*/
/* pub key */
pEVPKey = EVP_PKEY_new();
pRSA = RSA_generate_key(bits, E, NULL, NULL);
EVP_PKEY_assign_RSA(pEVPKey, pRSA);
iRV = X509_REQ_set_pubkey(pX509Req, pEVPKey);
// EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **pEVPKey, pem_password_cb *cb, void *u);
/* attribute */
strcpy(szBuf,szAltName);
nLen = strlen(szBuf);
iRV = X509_REQ_add1_attr_by_txt(pX509Req, "subjectAltName", V_ASN1_UTF8STRING, szBuf, nLen);
strcpy(szBuf, szKeyUsage);
nLen = strlen(szBuf);
iRV = X509_REQ_add1_attr_by_txt(pX509Req, "keyUsage", V_ASN1_UTF8STRING, szBuf, nLen);
strcpy(szBuf, szExKeyUsage);
nLen = strlen(szBuf);
iRV = X509_REQ_add1_attr_by_txt(pX509Req, "extendedKeyUsage", V_ASN1_UTF8STRING, szBuf, nLen);
strcpy(szBuf, szComment);
nLen = strlen(szBuf);
iRV = X509_REQ_add1_attr_by_txt(pX509Req, "nsComment", V_ASN1_UTF8STRING, szBuf, nLen);
md = EVP_sha1();
iRV = X509_REQ_digest(pX509Req, md, mdout, &nModLen);
// printf("摘要:\n");
for (int i = 0; i < strlen(mdout); i++) {
// printf("%x",mdout[i]);
}
iRV = X509_REQ_sign(pX509Req, pEVPKey, md);
if(!iRV)
{
printf("sign err!\n");
X509_REQ_free(pX509Req);
return -1;
}
// unsigned char *signChart = pX509Req->signature->data;
// printf("\n签名:%s\n",signChart);
// char *skey;
// int n;
// pPemBIO = BIO_new(BIO_s_mem());
// BIO_read(pPemBIO, skey, n);
// printf("skey:%s",skey);
// 写入文件PEM格式
// pBIO = BIO_new_file("certreq.txt", "w");
// PEM_write_bio_X509_REQ(pBIO, pX509Req, NULL, NULL);
// BIO_free(pBIO);
// 2.1 返回PEM字符 PKCS10证书请求
pPemBIO = BIO_new(BIO_s_mem());
// PEM_write_bio_X509_REQ(pPemBIO, pX509Req, NULL, NULL);
PEM_write_bio_X509_REQ(pPemBIO, pX509Req);
BIO_get_mem_ptr(pPemBIO,&pBMem);
if(pBMem->length <= nCSRSize)
{
memcpy(pCSR, pBMem->data, pBMem->length);
}
BIO_free(pPemBIO);
char priKey[2048] = {0};
char passwd[] = "123"; // 对私钥进行加密的密码
pPemBIO = BIO_new(BIO_s_mem());
// if (PEM_write_bio_RSAPrivateKey(pPemBIO, pRSA, EVP_des_ede3(), (unsigned char *)passwd, 4, NULL, NULL)!=1) {
// printf("private key error\n");
// }
if (PEM_write_bio_RSAPrivateKey(pPemBIO, pRSA,NULL, NULL, 0, NULL, NULL)!=1) {
printf("private key error\n");
}
BIO_get_mem_ptr(pPemBIO,&pBMem);
if(pBMem->length <= nCSRSize)
{
memcpy(privateKey, pBMem->data, pBMem->length);
}
BIO_free(pPemBIO);
// printf("私钥:\n%s\n",privateKey);
/* DER编码 */
//nLen = i2d_X509_REQ(pX509Req, NULL);
//pDer = (unsigned char *)malloc(nLen);
//p = pDer;
//nLen = i2d_X509_REQ(pX509Req, &p);
//free(pDer);
// 验证CSR
OpenSSL_add_all_algorithms();
iRV = X509_REQ_verify(pX509Req, pEVPKey);
if(iRV<0)
{
printf("verify err.\n");
}
X509_REQ_free(pX509Req);
// 输出pkcs10CSR证书请求
// printf("CSR:\n%s", pCSR);
return nCSRSize;
}
最后在OC中代码调用方式:
/* 生成pkcs10 证书请求 格式:/CN=参数1/O=参数2/OU=参数3……
* 例如:"/CN=www.cicc.com/O=cicc.com/OU=IT/ST=Beijing City/L=beijing/C=CN/[email protected]"
* CN: 通用名称,域名 Common Name
* O: 组织 Organization
* OU: 部门 Organizational Unit
* ST: 省份 State
* L: 城市 Locality
* C: 国家 Country
*/
NSString *info =@"/CN=www.cicc.com/O=cicc.com/OU=IT/ST=Beijing City/L=beijing/C=CN/[email protected]";
char chDN[255] ;
memcpy(chDN, [userInfo cStringUsingEncoding:NSASCIIStringEncoding], 2*[userInfo length]);
char chCSR[2048] = {0};
char privateKey[2048] = {0};
long int rv = GenCSR(chDN, strlen(chDN), chCSR, sizeof(chCSR),privateKey);
NSString* pkcs10=[NSString stringWithFormat:@"%s",chCSR];
NSString* priKey=[NSString stringWithFormat:@"%s",privateKey];
NSArray *resultArray = [NSArray arrayWithObjects:pkcs10,priKey, nil];
// 返回的数组的第一个为PKCS10 CSR证书请求,第二个值为(未加密的)私钥
最后给出请求证书的结果参考和私钥的结果参考:
-----BEGIN CERTIFICATE REQUEST-----
MIIDXzCCAkcCAQMwgY4xFTATBgNVBAMMDHd3dy5jaWNjLmNvbTERMA8GA1UECgwI
Y2ljYy5jb20xCzAJBgNVBAsMAklUMRUwEwYDVQQIDAxCZWlqaW5nIENpdHkxEDAO
BgNVBAcMB2JlaWppbmcxCzAJBgNVBAYMAkNOMR8wHQYJKoZIhvcNAQkBDBA5MzQ4
MDA5OTZAcXEuY29tMIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAvlzj
emgqbuIIceej1iVqbrAcN+JM59kichhUv+dZNL2hWf3ya1xYZ+GCNZD52u8L6lmD
N+OKMGYfGgrKBcfvIIOGo99RnB0ipsq2lDd/pzfc26Ao1G2v+i1MNB4g1LeLAUnr
sC+alSAZoh0fuj2TmSRp0rbB/IRezqGt/S2zmYhebAE6+orn74bYtQ/H3Bu+ldOt
8KLn/FbmaRe8T5OJc0ayGE8dqEjQj0S9VHyCJVFumTwG18ZNdJyjRwn1K3lzX8gj
hpgtSaKHQwv4VpYNKDKrEBjsBYKPNVl0teSygFIXE2fmLoar43BfVM0LoVCKqXmt
H/OAENpCMmLSOgxYsQIBA6CBjDAZBgNVHRExEgwQRE5TOnd3dy5jaWNjLmNvbTAf
BgNVHSUxGAwWc2VydmVyQXV0aCwgY2xpZW50QXV0aDAjBglghkgBhvhCAQ0xFgwU
Q3JlYXRlIGJ5IGxpdWNoZW5nbGkwKQYDVR0PMSIMIGRpZ2l0YWxTaWduYXR1cmUs
IG5vblJlcHVkaWF0aW9uMA0GCSqGSIb3DQEBBQUAA4IBAQADg6ZJbaKhjirLbRj8
vanggxrmb4vyMx4qrNqEuem5dvbHJXu8qfzd3NkW8oYEOlyz45ndjP1ODQ6O1Bfa
dHJSgfqcPbqU3ZFHttXkgVKrbkJbzGWH2olWkeLBzbYqo0rjXtWmFdacw+O1+EJ/
VHzh2KyJLfo/w3wBmD5mFQpHwj5gMaEmZULrfgI6adR4yBacEafZQq1dCR67xwWB
AEh8xrOSsy85Bjm382+GbDdJIzHmZZcbo9kgjSjh3OASLRL8dsSupVedlJ/jQZpB
NII/8Llf9tVwcGnThAJ1P8zIp7Q8j9ErT+U15w0zTNoQ3yUny02RNR0/9xzZqrsg
1K6F
-----END CERTIFICATE REQUEST-----
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAvlzjemgqbuIIceej1iVqbrAcN+JM59kichhUv+dZNL2hWf3y
a1xYZ+GCNZD52u8L6lmDN+OKMGYfGgrKBcfvIIOGo99RnB0ipsq2lDd/pzfc26Ao
1G2v+i1MNB4g1LeLAUnrsC+alSAZoh0fuj2TmSRp0rbB/IRezqGt/S2zmYhebAE6
+orn74bYtQ/H3Bu+ldOt8KLn/FbmaRe8T5OJc0ayGE8dqEjQj0S9VHyCJVFumTwG
18ZNdJyjRwn1K3lzX8gjhpgtSaKHQwv4VpYNKDKrEBjsBYKPNVl0teSygFIXE2fm
Loar43BfVM0LoVCKqXmtH/OAENpCMmLSOgxYsQIBAwKCAQB+6Jem8BxJ7AWhRRfk
GPGfIBLP7DNFO2xMEDh/75DN08DmqUxHkuWallbOYKaR9LKcO6zP7QbK7r9msdwD
2p9rAlnClOESvhcZ3Hm4JP/Ez+iSasXi88qmyN14FBXjJQdWMUfKymcOFWZsE2p8
KQ0QwvE3JIFTAunfFnP+Hne7rxhaObitPnLyuZaqtT1lVVB/YAAGr1trXzaR+iWU
NJH1bQ0GXXyGCpy1q4wSeu2ZL1mMfaxYCXTpUYTHCSZkCsqv/wnZhuQY09bHIEkL
4XlxF4Te3h3ktEb3jqvS68seYLYG+H6Pfh85e3+2rSI5TprnmAIXylLUhCAEFuBi
F3pLAoGBAOYj1UqPUQtHUtAKSnVqwoi8nWVMNZu2dMkZ1zrj1MfG9i5U/tFZOY6v
pSDU50BroX16e3M9/JAqbY3nj9tQmyV1qFRfFUPTrm97NZaHFbP3l8t2PbsxR9Yl
YLKIS4cQM0kaUzdCypGjv27Lq5Dh1HoWr+Ghe6rc4yA4jZ/2YVwVAoGBANPA1Vtn
XDA8HaaqtXZZWT0aJkiaZkMk00t0SUkNb+a8LPA5xBGF/1bPHkpj3N1T6OrMBBEV
u43sNM407GBEzh3iIUBhOJNQtlWdJfQ9riMG95hLjgP9LE6cosBxN3rCjbzun/EU
futpd7D3Hcdphig3ZchayFj1MPHzsuGwh8UtAoGBAJltONxfi1zaNzVcMaOcgbB9
vkOII70kTdtmj3yX4y/Z+XQ4qeDmJl8fw2s4mirya6j8UkzT/bVxnl6aX+eLEhj5
GuLqDi03yZ+neQ8EuSKlD9z5fnzLhTluQHcFh69gIjC8N3oshwvCf58ycmCWjaa5
ypZrp8c97MAls7/5lj1jAoGBAI0rOOeaPXV9aRnHI6Q7kNNmxDBm7tdt4jJNhjCz
n+8oHfV72AuuqjnfaYbtPejim0cyrWC5J7Pyzd7N8urYiWlBa4BA0GI1zuO+GU1+
dBdZ+mWHtAKoyDRobIBLelHXCSifFUti/0ebpSCkvoTxBBrPmTA8hZCjdfaid0Eg
WoNzAoGAdVakWYBw5J1qNRjqHRd0/RWRQWgVwr2De8hSCDMRv53wDI91YJruraQz
jggoHTAL0Db7G409aSbjvH/udQtNC6ySP/QpweMPWXsdYBDesoBfeUVcoYGjOgoH
t4Ecd9RhWesnDAeLUxYXyB/fKlHQhLiTYlO7GRJ+lYmYB/xJYN8=
-----END RSA PRIVATE KEY-----