OpenSSL制作中文证书专题

OpenSSL制作中文证书专题
2008-04-22 18:49
经常看见有人为制作中文证书的事情头痛,借论坛新张之际,本人把摸索中文证书制作的过程公开出来供大家参考,不当之处敬请提出批评。
早在N年以前,本人就尝试用java写了一个小程序用来给自己制作证书玩的(那时候还不知道有openssl这个好东东),因为要依赖java环境运行而且中文信息显示乱码等问题,决定在网上找一个合适的开发库,一番比较后,在眼花缭乱中选择了openssl。
刚接触openssl那些迷茫和....就不提了,意料中的事,很快就开始看人家的范例程序:

  X509_NAME_add_entry_by_txt(name,"C", MBSTRING_ASC, "UK", -1, -1, 0);
  X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, "OpenSSL Group", -1, -1, 0);

哇,还是蛮简单的嘛,试试看--My God! 中文还是乱码!
别着急,意外发现有个牛人写了一个MiniCA,还有源码,先运行看看,还不错,中文出来了,显示正常!
虽然自己制作的根证书不能用,虽然....但是还是蛮有兴致地制作了好一些证书,开始测试着玩....
第一个测试就卡壳了,Outlook不认帐!
去看MiniCA的源码吧:
原来是使用了X509_NAME_add_entry_by_NID()函数,源码在处理字符串长度的时候出了点小瑕疵,于是修改了几行使长度准确无误(过程略),重新编译测试,结果Outlook还是不卖帐,又改一下,强制emailAddress域使用MBSTRING_ASC,还是不行。
无奈,差点要放弃openssl了,仔细分析了从一些网站申请的证书,用makecert.exe又试制了一些,发现他们都是使用Unicode编码而不是UTF-8!
阅读openssl的文档,看到这么一段文字:
NOTES
  The use of string types such as MBSTRING_ASC or MBSTRING_UTF8 is
  strongly recommened for the type parameter. This allows the internal
  code to correctly determine the type of the field and to apply length
  checks according to the relevant standards. This is done using
  ASN1_STRING_set_by_NID().
于是把源码中的B_ASN1_UTF8STRING改为MBSTRING_UTF8--ok,Outlook通过了,只是CountryName域的“中国”消失了!
再详细观察一下所生成的证书,内部居然是Unicode编码!
转换为UTF-8之前还先要转成Unicode呢,有什么必要这么绕来绕去呢,直接用Unicode好不好?
可是openssl没有定义Unicode,不过有一个MBSTRING_BMP和Unicode正好是反序编码,这就好办了:
int Add_Name(X509_NAME *x509name, int type, USHORT *iput)//中英文处理
{
  int strType = 0;
  UCHAR *pp = (UCHAR *)&iput[1];
  if(*iput == 0) return 0;
  if(*pp)   //Unicode字串,须转换成BMP,:-(
  {
    pp++; *iput *=2;
    do { pp[strType] = pp[strType+2]; strType +=2;}
    while((USHORT)strType != *iput);
    strType = MBSTRING_BMP;
  }
  else { strType = MBSTRING_ASC; pp +=2;}
  return X509_NAME_add_entry_by_NID(x509name,type,strType,pp,(int)*iput,-1,0);
}
其中*iput指向一个结构:
  USHORT String_Length;//字符串实际长度(注意:是“字符串长度”而不是字节数,在Unicode情况下,要乘以2才是字节数)
  USHORT String_Type;//字符串类型0=ASCII,1=Unicode
  UCHAR String_Str[];//字符串本身
把证书主题的每个域先按这个结构来处理好,然后组成一个总的结构,用以下的语句来调用Add_Name(),主题就正确形成了:
  ret=Add_Name(xname,NID_pkcs9_emailAddress,&DNSInfo->MAIL_l);
  ret=Add_Name(xname,NID_commonName,&DNSInfo->CN_l);
  ret=Add_Name(xname,NID_organizationalUnitName,&DNSInfo->OU_l);
  ret=Add_Name(xname,NID_organizationName,&DNSInfo->O_l);
  ret=Add_Name(xname,NID_localityName,&DNSInfo->L_l);
  ret=Add_Name(xname,NID_stateOrProvinceName,&DNSInfo->ST_l);
  ret=Add_Name(xname,NID_countryName,&DNSInfo->C_l);
这样改可以后,CountryName域取“中国”仍然是不行的,那么还按标准取“CN”好了。
经多方面测试,这样做出来的证书完全正常。
最后提一下关于openssl.exe制作中文证书的方法,本人曾留意到openssl.conf文件里是可以设定为使用UTF-8编码的,可惜Windows命令行下不可能直接输入UTF-8编码的字符,因此无法验证,后来在Linux环境下测试,因为Linux的终端就是使用UTF-8编码,所以不用做任何改编即可得到中文证书。

你可能感兴趣的:(OpenSSL制作中文证书专题)