X.509证书DN之详解
X.509使用DN(Distinct Name)来唯一标识一个实体,其功能类似我们平常使用的ID,不过不同的是,DN不再是类似 123456 这样得数字标识,而是采用多个字段来标识一个实体,例如”CN=老所,C=CN”,这样做的好处在于方便匹配到诸如LDAP一样的目录服务中。那么,DN的字段是否可以随意增加呢?比如我能否在”CN=老所,O=测试公司”这样一个DN上再增加一个ID属性,变成”CN=老所,O=测试公司,ID=123456″呢?
动手测试。首先采用微软的 XEnroll 组件来进行测试。XEnroll 是微软平台下的 ActiveX 控件,提供证书的加入服务,比如创建 PKCS#10 证书请求。我们可以用 Javascript 来调用这个 ActiveX 控件:
var sOID = '';
var sDN = 'CN="老所",O="测试公司",ID="123456"';
alert(sDN);
var sPKCS10 = oXEnroll.createPKCS10(sDN, sOID);
其中,createPKCS10 函数接受两个参数,第一个是字符串类型的DN,第二个是用以标识该证书请求的用途的OID,这里,我们不在乎该证书的用途,所以就设为空好了。通过 IE 运行该段代码后,我得到了 object Error,看来,不能这样简单地添加DN字段。
于是开始搜索关于 X.509 DN 的信息。原来,X.509 证书里的 DN 属性,都是一些基于 ASN1 编码的对象,也就是说,对于我们熟知的 CN 属性,或者说 commonName 属性,程序是无法从证书里获得的,证书里是不会写诸如 CN=xxx 这样的信息的。所有字段都被表示为该字段类型的 OID 。OID 则是 ASN1 对象的统一表示方法,这种方法采用树状结构,由国际组织统一管理。一个公司如果要增加一个字段类型,并希望被广泛采用,它必须像管理 OID 的组织提出申请,就像我们常用的域名申请一样。
在 www.oid-info.com 这个网站,我们可以查询所有注册过的 OID 信息。这个网站提供两种方式的查询:按树状结构展开各级 OID,或者直接填写诸如”2.3.4″这样的 OID 来进行查询。
假如我们展开 2.5.4 这层 OID,我们就会发现很多我们常用的 DN 字段:
- 2.5.4.3 - commonName
- 2.5.4.4 - surname
- 2.5.4.13 - description
- 2.5.4.10 - organizationName
- ……
既然如此,那我们就采用 OID 形式来添加属性类型吧,比如我使用 OID=2.5.4.888,这是一个未被注册的 OID,可以用来测试:
var sOID = '';
var sDN = 'CN="老所",O="测试公司",2.5.4.888="123456"';
alert(sDN);
var sPKCS10 = oXEnroll.createPKCS10(sDN, sOID);
运行该脚步,成功了,我们从机器里的证书申请库里找到该证书申请,查看其属性:
可以看出,虽然用 OID 添加属性成功了,但这个 OID 并不能被翻译成方便我们阅读的形式,就像我们只能使用 IP 而无法使用域名一样。要让该证书显示 ID= 而不是 2.5.4.888=,我们必须要:
- 向标准组织注册 2.5.4.888 为 ID;
- 通知微软,修改其 Windows 程序,将该 OID 翻译为 ID
显然,这非常不现实,呵呵。
接下来,我们再用 openssl 来测试一下。我选用的是 pyOpenSSL,一个 openssl 的 Python 包装库:
>>> from OpenSSL import crypto >>> req = crypto.X509Req() >>> subject = req.get_subject() >>> setattr(subject, 'CN', 'soloman') >>> setattr(subject, 'ID', '123456') Traceback (most recent call last): File "", line 1, in AttributeError: No such attribute >>>
看来,pyOpenSSL也只认识 CN,不认识 ID。查询了一下 pyopenssl 的源代码:
static int
crypto_X509Name_setattr(crypto_X509NameObj *self, char *name, PyObject *value)
{
int nid;
int result;
char *buffer;
if ((nid = OBJ_txt2nid(name)) == NID_undef)
{
PyErr_SetString(PyExc_AttributeError, "No such attribute");
return -1;
}
......
可以看出,pyOpenSSL 在设置 DN 的时候,首先调用 openssl 的 OBJ_txt2nid()函数来将方便我们记忆的对象名字翻译成 OID, 然后再翻译成 openssl 程序里自己的 NID。
那么继续查询 openssl 的源代码,在 objects.h 文件里我们找到了所有 DN 字段的定义:
......
#define SN_commonName "CN"
#define LN_commonName "commonName"
#define NID_commonName 13
#define OBJ_commonName OBJ_X509,3L
#define SN_countryName "C"
#define LN_countryName "countryName"
#define NID_countryName 14
#define OBJ_countryName OBJ_X509,6L
#define SN_localityName "L"
#define LN_localityName "localityName"
#define NID_localityName 15
#define OBJ_localityName OBJ_X509,7L
......
这里,openssl 为每个常用的 DN 属性定义了4个内容:SN, LN, NID, OBJ。其中 SN 表示 Short Name, LN 表示 Long Name,都是一个 OID 方便我们阅读的名称的两个形式,而 OBJ 则定义了其 OID 信息。
在 openssl/crypto/asn1/a_strnid.c 这个文件里,绑定了 openssl 能够支持并翻译的 DN 字段:
static ASN1_STRING_TABLE tbl_standard[] = {
{NID_commonName, 1, ub_common_name, DIRSTRING_TYPE, 0},
{NID_countryName, 2, 2, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
{NID_localityName, 1, ub_locality_name, DIRSTRING_TYPE, 0},
{NID_stateOrProvinceName, 1, ub_state_name, DIRSTRING_TYPE, 0},
{NID_organizationName, 1, ub_organization_name, DIRSTRING_TYPE, 0},
{NID_organizationalUnitName, 1, ub_organization_unit_name, DIRSTRING_TYPE, 0},
{NID_pkcs9_emailAddress, 1, ub_email_address, B_ASN1_IA5STRING, STABLE_NO_MASK},
{NID_pkcs9_unstructuredName, 1, -1, PKCS9STRING_TYPE, 0},
{NID_pkcs9_challengePassword, 1, -1, PKCS9STRING_TYPE, 0},
{NID_pkcs9_unstructuredAddress, 1, -1, DIRSTRING_TYPE, 0},
{NID_givenName, 1, ub_name, DIRSTRING_TYPE, 0},
{NID_surname, 1, ub_name, DIRSTRING_TYPE, 0},
{NID_initials, 1, ub_name, DIRSTRING_TYPE, 0},
{NID_serialNumber, 1, ub_serial_number, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
{NID_friendlyName, -1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK},
{NID_name, 1, ub_name, DIRSTRING_TYPE, 0},
{NID_dnQualifier, -1, -1, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
{NID_domainComponent, 1, -1, B_ASN1_IA5STRING, STABLE_NO_MASK},
{NID_ms_csp_name, -1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK}
};
可以看出,如果要让我们的基于 openssl 的程序支持一个我们自定义的属性,我们得修改 openssl 源代码,添加我们的新字段的”名称-OID” 绑定(可以只有Long Name),然后重新编译 openssl 和我们的程序,使其能使用新的属性。
我们回过头来想想,为什么标准化组织没有为我们提供一个类似 ID 这样的属性呢?这是因为,DN本来就是作为 ID 来使用的,它把一些属性绑定起来,来标识一个实体,如果再有一个通用的 ID 属性,逻辑上就冲突了。当然针对具体的应用,你可以注册一个特殊的 ID 属性,比如 openID ,但这需要实力雄厚的公司来进行注册和推广。
然而实际应用中,当我们想将证书系统与某个特别的应用中的某个 ID 进行绑定,而又不具备向国际组织进行 OID 注册和推广的能力时,我们完全可以利用现有的属性来进行实现,比如,针对这个需求,我可以设计出两个方案:
- 方案一:CN=老所@123456
- 方案二:CN=123456, surname=老, givenName=所
- 方案三:CN=123456, name=老所
- 方案四:CN=老所, description=123456
方案一,通过一定的方式,将用户的姓名和ID组合到CN里,然后在应用中解析出ID,这个方法不是很便于用户书写,用户在填写证书申请资料的时候必须注意符合该规范。
方案二与三的概念就是用CN来记录ID,毕竟,Common Name 嘛,随你如何定义。而用户的姓名由 surname + givenName 或者 name 来指定。该方法通过了 openssl 测试:
>>> req = crypto.X509Req() >>> subject = req.get_subject() >>> setattr(subject, 'surname', 'Lao') >>> setattr(subject, 'givenName', 'Suo') >>> setattr(subject, 'name', 'Lao Suo')
但是,XEnroll 仿佛不支持 surname + givenName 或者 name 属性,其文档上说明是支持 X.500 规范,而不是 X.509 规范。
方案四,则利用了 description 属性来记录和具体应用相关的讯息,比如我们的 ID,该方法通过了 openssll 的测试:
>>> req = crypto.X509Req() >>> subject = req.get_subject() >>> setattr(subject, 'CN', 'Lao Suo') >>> setattr(subject, 'description', '123456')
和 XEnroll 的测试:




十二月 4th, 2008 at 01:26
我做沙发,问个问题,这个x.509证书是怎么获得的,有免费的吗?你的文章我还是看不太明白,你们用这个做什么,不好意思问了好几个问题,赫赫
十二月 4th, 2008 at 01:53
@basil
X.509 证书用途非常广泛,比如我们常用的网上银行,就是使用的X.509证书,如果该证书保存成文件,就叫文件证书,如果保存到USB里,就是诸如招商银行所谓的“移动证书”。我们常用的支付宝也是使用的X.509证书。当然,还有我们常用的SSL网站,用X.509证书来确保网络通讯的安全。
X.509证书基于不对称密钥加密技术,将公钥和某个人的身份(也就是我文中所说的实体,用DN来表示)绑定在一起,并通过第三方认证机构(叫做CA)来用数字签名确保这个绑定是正确的,也就是说CA保证了这个公钥就是这个DN表示的人。
同样的基于不对称密钥的认证技术,除了这个CA(也叫PKI)技术,还有一个架构是RSA公司经营的PGP技术,这个PGP技术和PKI技术很不一样,他没有第三方CA,而是靠人与人之间互相认证,形成一个 ad hoc 认证网络,当年拉登策划911就用的 PGP 软件来确保邮件的加密与防篡改,为此,RSA公司还被美国政府起诉,有兴趣的话你可以了解下:)
十二月 4th, 2008 at 01:55
@basil
X.509证书用途很广泛,比如用于邮件的签名和加密,这个在Windows 的邮件系统里叫用户电子标识,可以免费注册的,网上也有一些免费为个人提供的个人邮件证书(当然是X.509),你可以去搜索下“免费个人邮件证书” 之类的关键字,应该能找到一些免费的CA
十二月 4th, 2008 at 01:59
@basil
再说明白一点吧,X.509证书谁都可以自己生成,用openssl即可,但这种情况下,你是用自己的CA来签发得(CA也是一个证书,当然还有与该证书匹配的私钥),这种情况下,别人无法信任你,所以要有第三方具有公证能力的机构来签发你的证书,这样的证书才能被大家接受。
证书的目的就是告诉别人,这个私钥是DN表示的这个人的,所以如果签发证书的CA没有广泛的公证性,别人就不确保这个私钥就是你,呵呵。
十二月 4th, 2008 at 02:04
@basil 改天有空了我再写个用 x.509 证书来对邮件签名和加密的文章,到时候你就明白 证书是个怎么回事了:)
十二月 4th, 2008 at 08:29
反正我不懂,我只是来问点弱智问题。很多软件也带证书,跟这个一样吗?你这样说的话这样的证书也有破解或者遗漏之处,是不是会对网上交易造成隐患?
十二月 4th, 2008 at 09:00
太看不懂了。。。。我是白痴!
十二月 4th, 2008 at 11:11
完全不懂,只会用~~
十二月 4th, 2008 at 11:42
有点不懂,不清楚说什么.
十二月 4th, 2008 at 12:18
这么深奥啊,看不懂啊老所,太多了,我不懂什么证书,我最近搞诺基亚的证书签名的,安装了很多好玩的
十二月 4th, 2008 at 13:34
只看了一点,像是一个结构体???
十二月 4th, 2008 at 14:22
我只知道LDAP,还是前几天刚听说的…老所你写的太专业了.
十二月 4th, 2008 at 19:49
完全看不懂 。。
我记得Thawte有免费的个人电邮证书,据说被所有客户端信任。但偶以前用Outlook Express测试还是有警告,得装Thawte的中级证书(系统中默认只有Thawte根证书)
话说还是PGP王道啊!!!
十二月 4th, 2008 at 20:15
老所真能折腾啊!!我送个故事吧!
浙江是个体经济发达的地方.自古如此.西施的爸爸是开小印染厂的.那时不知道什么 863计划,用的都是手工生产, 还大量使用氰化物和水银(那叫汞),把西湖搞得一塌糊涂.周围的老百姓到省政府那里去抗议,省长说,勾大王要大家发展经济,西氏印染联合株式会社是我省的利税大户,要是它不开工,大王的计划完不成,我个人的进退是小事,我怕咱们的经济搞不上去,下次发大水的时候,发达地区的洪水还要往咱们这里排.虽然中央夸我们省顾全大局,可是吃亏的还是大家不是?!于是骂归骂,西氏印染厂的污水照排不误。西施其实也是个苦孩子.妈妈死得早,爸爸又找了一个.好容易初中毕业了, 爸爸说,女孩子上学有什么用?不给她上了.要她上厂里做工. 西施年纪小,不能干重活.于是她拿着篮子去溪边洗(那叫浣纱). 溪边臭气熏天.鱼儿都死了.干活的人都没有好气.看到西施来了,都指桑骂槐地嘴里不干不净起来.可怜的西施只能忍着. 水里的水银含量太高了.鱼儿的肉里也有大量的水银(其化石中的水银含量也大大超标,详见>1993年十月号P34-36),死鱼都沉了底.大伙连鱼都吃不上,便编故事说西施是灾星,她到过的地方必定要倒霉. 勾践知道了,便动起了坏注意.要西施嫁到吴国去.西施死活不肯.于是勾践找她爸爸,对他说,如果西施能够嫁到吴国去,他便是海外侨胞,可以进政协,还可以到临淄(相当于今天的香港)定居.西爸爸动了心,内外夹攻,西施含着泪,到吴国去了. 在到吴国的路上,她对范蠡哭诉了她的遭遇.范蠡很同情她,同时也爱上了她.他对她说,我等你.后来,他们过上了幸福的生活.
十二月 4th, 2008 at 22:55
读后感:看不懂!牛人写的天书!
十二月 5th, 2008 at 00:02
@老所
还是不太懂,再问个问题,假设n个客户端和某服务器端通信,如果使用x.509证书的话,是要1个还是n个,这个有没有免费的申请的?
十二月 5th, 2008 at 15:14
@leehow x.509是目前市面上最流行的证书,所以大部分应该都是。证书是和安全相关的,所有密码理论上都是能破解的,不过只要你正确使用,是可以完全信赖证书的安全性的。关键是,证书系统和普通的应用稍微复杂点,所以很多人用的不明不白,经常错误使用,比如对私钥的安全没有概念,这就造成了安全隐患。改天我写个通俗易懂的证书使用注意事项好了:)
十二月 5th, 2008 at 15:16
@Shawn @Elton Disney @u88 @醉倚西风 呵呵,谢谢大家回复哈,看不懂正常的,我这篇估计是要搞PKI开发的人才有用:)隔行如隔山嘛。改天写个通俗的,让大家注意个人网上信息安全。
十二月 5th, 2008 at 15:18
@iColor 证书是个抽象概念,X.509规范就是将该概念具体话,比如证书的各个内容怎么编码啊,什么的,所以,那就是个结构体嘛,恭喜你答对了,聪明的iColor同学。
十二月 5th, 2008 at 15:19
@i.robot LDAP没挺说过,Windows 的Active Directory 活动目录听说过么?这些都是目录服务,你在Outlook里可以添加目录服务的,也就是把你的通讯录放到服务器上。
十二月 5th, 2008 at 15:21
@小野大神 国内企业很少用PGP的,所以PKI还是市场很大的。有的证书是被默认信任的,那是因为微软windows默认装了一些CA的根证书。你在命令行输入‘mmc’,然后添加“证书”单元就可以看你机器上所有安装的证书。在IE里选 选项-》内容-》证书 也可以看到
十二月 5th, 2008 at 15:24
@老时 去年5月,福建厦门好像也有类似的事,不过你这个具体说的什么呢?不要发在这,email me: soloman817@gmail.com 呵呵。
十二月 5th, 2008 at 15:30
@basil 你这个例子是要把证书用作服务器客户端认证,这个用法有两种: 1. 服务器认证; 2. 客户端认证。
当我们进行服务器认证的时候,只需要服务器提供证书,让我们确保我们访问的服务器不是伪造的。这个用法只需要服务器有证书即可。
当我们进行客户端认证的时候,每个访问网站的用户必须有自己的证书,这样服务器才能确认你的身份,进而进行访问授权,这种情况下,每个客户需要1个证书。
服务器的证书还用于通讯加密。所以
如果你只需要通讯加密和服务器认证,就安装一个证书在服务器上即可(其DN包括你的域名)
如果你要使用证书来授权某些用户才能访问你的网站,每个人需要1个证书。这就类似你登录google邮箱,不在使用密码了,而是使用一个证书。所以每个人都必须有证书,就像每个人都必须有密码一样。
一般windows 默认信任的证书的签发都是一些商业公司运营的,不是免费的。你可以自己用openssl制作证书,然后把根证书导入windows,让其信任即可。
十二月 5th, 2008 at 15:31
@Soloman 笑话而已哈
十二月 5th, 2008 at 15:41
@老时 你的博客怎么访问不了了?又被河蟹了?
十二月 5th, 2008 at 16:04
@Soloman 可以访问啊?
十二月 5th, 2008 at 17:20
@Soloman 对对,就是期待你这个,我在网上用这些东西比较频繁,还是很心虚。
十二月 6th, 2008 at 00:23
@老所
哦,我自己做的证书,在客户端和服务器端访问没有问题吧?
十二月 7th, 2008 at 20:55
好象是什么证书之类的,感觉挺专业的.
十二月 18th, 2008 at 21:46
PKI的应用。