OpenSSL:一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。
pkcs#10:PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准。
What is PKCS? http://www.rsa.com/rsalabs/node.asp?id=2308
PKCS 目前共发布过 15 个标准:其中,
PKCS#10:证书请求语法标准。PKCS#10定义了证书请求的语法。证书请求包含了一个唯一识别名、公钥和可选的一组属性,它们一起被请求证书的实体签名(证书管理协议中的PKIX证书请求消息就是一个PKCS#10)。
Mac osx 系统自带了命令行工具生成p10证书:
具体参见这里:通过命令行生成p10证书
如果我们想要通过代码方式实现证书请求,就要需要用到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/
5、新建一个C File,命名为:header,Xcode会同时帮我们生成一个header.h的头文件。
在header.h中,我们引用一下头文件,并且声明需要用到的函数.
#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);
int GenCSR(char *pbDN, int nDNLen, char *pCSR, size_t nCSRSize);
6、在header.c中,实现刚才声明的三个函数。这里只列出GenCSR方法。
int GenCSR(char *pbDN, int nDNLen, char *pCSR, size_t nCSRSize)
{
char szAltName[] = "DNS:www.jinhill.com";
char szComment[] = "Create by Jinhill";
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};
char mdout[20];
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);
// subject pX509Name
iRV = X509_REQ_set_subject_name(pX509Req, pX509DN);
/* 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);
/* 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);
iRV = X509_REQ_sign(pX509Req, pEVPKey, md);
if(!iRV)
{
printf("sign err!\n");
X509_REQ_free(pX509Req);
return -1;
}
// 写入文件PEM格式
// pBIO = BIO_new_file("certreq.txt", "w");
// PEM_write_bio_X509_REQ(pBIO, pX509Req, NULL, NULL);
// BIO_free(pBIO);
//返回PEM字符
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);
/* 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);
return nCSRSize;
}
7、测试一下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
char chDN[255] = "/CN=www.jinhill.com/O=Beijing Jinhill Inc./C=CN";
char chCSR[2048] = {0};
int rv = GenCSR(chDN, strlen(chDN), chCSR, sizeof(chCSR));
printf("CSR:\n%s", chCSR);
}
可以看到我们生成的证书请求字符串已经打印出来了:
CSR:
—–BEGIN CERTIFICATE REQUEST—–
MIIDFjCCAf4CAQMwRjEYMBYGA1UEAwwPd3d3LmppbmhpbGwuY29tMR0wGwYDVQQK
DBRCZWlqaW5nIEppbmhpbGwgSW5jLjELMAkGA1UEBgwCQ04wggEgMA0GCSqGSIb3
DQEBAQUAA4IBDQAwggEIAoIBAQDjf4sXLTio7Qzo9y3eFQsNSzmj/Dxu+DQX4wt3
Nxk2+ExEP5cq/qPW/GuFJbvBwuxOst3J7wraGgbfSqG5s+cLdy6r4S1Y9HM3Nr23
MsiKqpVwTvG03HhZk0lAvkeM/0eb2XjijRa3f8Ndrvh56uIldDiWMgGPnFAlv474
qonEk0tLrBjvKpS+jBB4T03nBOLCdoAO5UNsSTMnt/4ZaakLu+cgxsaV/bh8UxNS
ff8zP9pGBuHAGl6jA+D/zwK/u8twKI+5zYbdMprBfIEk5f7hYpmFIi/IB85dAkUA
XVv2JhQ00Yup5/FSNWM3y4f/f4bE7dHEEWolxA0dT222ggZBAgEDoIGMMBwGA1Ud
ETEVDBNETlM6d3d3LmppbmhpbGwuY29tMB8GA1UdJTEYDBZzZXJ2ZXJBdXRoLCBj
bGllbnRBdXRoMCAGCWCGSAGG+EIBDTETDBFDcmVhdGUgYnkgSmluaGlsbDApBgNV
HQ8xIgwgZGlnaXRhbFNpZ25hdHVyZSwgbm9uUmVwdWRpYXRpb24wDQYJKoZIhvcN
AQEFBQADggEBAEYdkInQcbHdb+jLbEcpM3wBYlObckAMhpzldcg74TXzsm9RfKqO
hAzmw0MbVAEelCEX42h94AQ9AYFe6WRYUUvmcgPnaQqOHfOaE3cZK0tjB/H2Whj9
mDz+HA+xoQgRpPbkF19PleJSMp4jL2/a284AKHqE+CBSx+WAiKJmtxnunHejZn7V
pqbc0eypXKFxOvZKK3WEcVuWmSpacydvSuoKH2cprBJP1fZaXOEPwMgidEEiG60F
nhbreYnTKXriEzGY0KvN7PtHwXLVr0wKWL6SYpY8xm3pMWSmUUiQz35unnw1VGqF
6Mt9g0Ir9Dd/IUFjW8XC8L1dvyYIwhVKF50=
—–END CERTIFICATE REQUEST—–
后记:关于生成原理其实很简单,可以参考这篇文章,已经写得非常详细:
证书请求原理