欢迎来到黑龙江省数字证书认证有限公司官方网站

数字证书

什么是.X509及如何解析X509证书

作者:admin 发布时间:2017-12-03

x509


  X.509是一种非常通用的证书格式。所有的证书都符合ITU-T X.509国际标准;因此(理论上)为一种应用创建的证书可以用于任何其他符合X.509标准的应用。在一份证书中,必须证明公钥及其所有者的姓名是一致的。对X.509证书来说,认证者总是 CA或由CA指定的人,一份X.509证书是一些标准字段的集合,这些字段包含有关用户或设备及其相应公钥的信息。X.509标准定义了证书中应该包含哪些信息,并描述了这些信息是如何编码的(即数据格式),所有的X.509证书包含以下数据: 1、X.509版本号:指出该证书使用了哪种版本的X.509标准,版本号会影响证书中的一些特定信息。目前的版本是3。
 
  2、证书持有人的公钥:包括证书持有人的公钥、算法(指明密钥属于哪种密码系统)的标识符和其他相关的密钥参数。
 
  3、证书的序列号:由CA给予每一个证书分配的唯一的数字型编号,当证书被取消时,实际上是将此证书序列号放入由CA签发的CRL(Certificate Revocation List证书作废表,或证书黑名单表)中。这也是序列号唯一的原因。
 
  4、主题信息:证书持有人唯一的标识符(或称DN-distinguished name)这个名字在 Internet上应该是唯一的。DN由许多部分组成,看起来象这样:
 
  CN=Bob Allen, OU=Total Network Security Division
 
  O=Network Associates, Inc.
 
  C=US
 
  这些信息指出该科目的通用名、组织单位、组织和国家或者证书持有人的姓名、服务处所等信息。
 
  5、证书的有效期:证书起始日期和时间以及终止日期和时间;指明证书在这两个时间内有效。
 
  6、认证机构:证书发布者,是签发该证书的实体唯一的CA的X.500名字。使用该证书意味着信任签发证书的实体。(注意:在某些情况下,比如根或顶级CA证书,发布者自己签发证书)
 
  7、发布者的数字签名:这是使用发布者私钥生成的签名,以确保这个证书在发放之后没有被撰改过。
 
  8、签名算法标识符:用来指定CA签署证书时所使用的签名算法。算法标识符用来指定CA签发证书时所使用的公开密钥算法和HASH算法。
 
  X.509证书格式
 
   为了利用公共密钥这种密码系统,必须将公共密钥分发出去。最通用的一种签名证书格式被称为X.509格式。X.509格式的证书被VeriSign、微软、网景和其他许多公司广泛应用于对电子邮件消息进行签名,对程序代码进行认证,以及对许多其他类型的数据进行认证等等。X.509标准是由国际电话标准机构,即国际电报电话咨询委员会(CCITT)提出的用于目录服务的X.500系列建议的组成部分。
 
  X.509证书的具体结构是用一种形式化表示来描述的,称为"抽象语法表示法#1"(abstract syntax notation)即ASN.1。图9-13显示了第三版X.509格式的ASN.1定义。虽然具体的语法对我们并不重要,但是你可以看到,ASN.1为证书文件的结构给出了精确的定义。"基本编码规则"(basic encoding rules),即BER,精确地描述了如何将该结构保存为二进制文件。也就是说,BER描述了如何对整数、字符串、位串以及诸如SEQUENCE、CHOICE和OPTIONAL的结构进行编码的方法。
 
  
[Certificate ::= SEQUENCE {
 
  tbsCertificate TBSCertificate,
 
  signatureAlgorithm AlgorithmIdentifier,
 
  signature BIT STRING
 
  }
 
  TBSCertificate ::= SEQUENCE {
 
  version [0] EXPLICIT Version DEFAULT v1,
 
  serialNumber CertificateSerialNumber,
 
  signature AlgorithmIdentifier,
 
  issuer Name,
 
  validity Validity,
 
  subject Name,
 
  subjectPublicKeyInfo SubjectPublicKeyInfo,
 
  issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
 
  -- If present, version must be v2or v3
 
  subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
 
  -- If present, version must be v2or v3
 
  extensions [3] EXPLICIT Extensions OPTIONAL
 
  -- If present, version must be v3
 
  }
 
  Version ::= INTEGER {
 
  v1(0), v2(1), v3(2)
 
  }
 
  CertificateSerialNumber ::= INTEGER
 
  Validity ::= SEQUENCE {
 
  notBefore CertificateValidityDate,
 
  notAfter CertificateValidityDate
 
  }
 
  CertificateValidityDate ::= CHOICE {
 
  utcTime UTCTime,
 
  generalTime GeneralizedTime
 
  }
 
  UniqueIdentifier ::= BIT STRING
 
  SubjectPublicKeyInfo ::= SEQUENCE {
 
  algorithm AlgorithmIdentifier,
 
  subjectPublicKey BIT STRING
 
  }
 
  Extensions ::= SEQUENCE OF Extension
 
  Extension ::= SEQUENCE {
 
  extnID OBJECT IDENTIFIER,
 
  critical BOOLEAN DEFAULT FALSE,
 
  extnValue OCTET STRING
 
  }
 
  解析X509证书
 
  1.从磁盘上的证书文件中读取证书数据
 
  unsigned char* pbX509Data; // 证书数据
 
  unsigned long ulX509DataLen; // 证书数据长度
 
  2.获取CertContext
 
  PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, pbX509Data, ulX509DataLen);
 
  3.获取证书信息
 
  pCertContext->pCertInfo->dwVersion; // 证书版本号
 
  CRYPT_INTEGER_BLOB snBlob = pCertContext->pCertInfo->SerialNumber; // 证书SN
 
  CERT_NAME_BLOB issuerBlob = pCertContext->pCertInfo->Issuer; // 证书颁发者
 
  CERT_NAME_BLOB subjectBlob = pCertContext->pCertInfo->Subject; // 证书主题
 
  // 证书有效起始日期
 
  SYSTEMTIME sysTime;
 
  memset(&sysTime, 0, sizeof(sysTime));
 
  FileTimeToSystemTime(&pCertContext->pCertInfo->NotBefore, &sysTime);
 
  char szTime[128] = {0};
 
  sprintf_s(szTime, 128, "%d年%d月%d日 %d:%d:%d", sysTime.wYear, sysTime.wMonth, sysTime.wDay, sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
 
  // 证书有效终止日期
 
  memset(&sysTime, 0, sizeof(sysTime));
 
  FileTimeToSystemTime(&pCertContext->pCertInfo->NotAfter, &sysTime);
 
  memset(szTime, 0, sizeof(szTime));
 
  sprintf_s(szTime, 128, "%d年%d月%d日 %d:%d:%d", sysTime.wYear, sysTime.wMonth, sysTime.wDay, sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
 
  4.创建临时密钥容器
 
  HCRYPTPROV hTmpProv = NULL;
 
  CryptAcquireContext(&hTmpProv, "My_Temporary_Container", NULL, PROV_RSA_AES, 0); // NULL表示使用系统默认CSP
 
  5.向容器中导入公钥,获取公钥句柄
 
  HCRYPTKEY hKey = NULL;
 
  CERT_PUBLIC_KEY_INFO certPubKeyInfo = pCertContext->pCertInfo->SubjectPublicKeyInfo;
 
  CryptImportPublicKeyInfo(hTmpProv, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, &certPubKeyInfo, &hKey);
 
  6.导出公钥(最好采用二次调用方式)
 
  unsigned char* pBuf = NULL;
 
  unsigned long ulBufLen = 0;
 
  CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, pBuf, &ulBufLen);
 
  pBuf = new unsigned char[ulBufLen];
 
  memset(pBuf, 0, ulBufLen);
 
  CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, pBuf, &ulBufLen);
 
  7.获取公钥信息
 
  unsigned char* p = pBuf + sizeof(PUBLICKEYSTRUC);
 
  (*(RSAPUBKEY*)p).bitlen; // 公钥模长(以bit为单位)
 
  (*(RSAPUBKEY*)p).pubexp; // 公钥的e(注意字节顺序)
 
  p += sizeof(RSAPUBKEY); // 公钥的n(注意字节顺序)
 
  8.清理工作
 
  delete[] pBuf;
 
  pBuf = NULL;
 
  CryptDestroyKey(hKey);
 
  CryptReleaseContext(hTmpProv, 0);
 
  CertFreeCertificateContext(pCertContext);