3

我正在尝试使用 javascript 在客户端为 PKCS#10 证书请求创建签名的 PKCS#7 消息。

PKCS#10 上有很好的例子:http: //blogs.msdn.com/b/alejacma/archive/2009/01/28/how-to-create-a-certificate-request-with-certenroll-javascript.aspx

但我需要创建 PKCS#7 并且无法弄清楚如何去做。CertEnroll 的官方文档中缺少示例(实际上根本没有):http: //msdn.microsoft.com/en-us/library/windows/desktop/aa374850 (v=vs.85).aspx

我最终得到了这段代码:

var XCN_CRYPT_STRING_BASE64REQUESTHEADER = 3;

var XCN_CERT_NAME_STR_NONE = 0;

var _certEnrollClassFactory = new ActiveXObject("X509Enrollment.CX509EnrollmentWebClassFactory");


ComposePKCS10Request: function (containerName, subject)
{
    // PKCS #10 certificate request
    var objRequest = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509CertificateRequestPkcs10");

    var objCSP = objCertEnrollClassFactory.CreateObject("X509Enrollment.CCspInformation");
    var objCSPs = objCertEnrollClassFactory.CreateObject("X509Enrollment.CCspInformations");

    //  Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
    objCSP.InitializeFromName("Microsoft Enhanced Cryptographic Provider v1.0");

    //  Add this CSP object to the CSP collection object
    objCSPs.Add(objCSP);

    // asymmetric private key that can be used for encryption, signing, and key agreement.
    var objPrivateKey = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509PrivateKey");

    //  Provide key container name, key length and key spec to the private key object
    objPrivateKey.ContainerName = containerName;
    //objPrivateKey.Length = 1024;
    objPrivateKey.KeySpec = 1; // AT_KEYEXCHANGE = 1

    //  Provide the CSP collection object (in this case containing only 1 CSP object)
    //  to the private key object
    objPrivateKey.CspInformations = objCSPs;

    // Initialize P10 based on private key
    objRequest.InitializeFromPrivateKey(1, objPrivateKey, ""); // context user = 1

    // X.500 distinguished name (DN)
    // The DN consists of a sequence of relative distinguished names (RDNs). Each RDN consists of a set of attributes, 
    // and each attribute consists of an object identifier (OID) and a value. The data type of the value is identified 
    // by the DirectoryString structure.
    var objDn = _certEnrollClassFactory.CreateObject("X509Enrollment.CX500DistinguishedName");

    // DN related stuff
    objDn.Encode(subject, XCN_CERT_NAME_STR_NONE);
    objRequest.Subject = objDn;

    return objRequest;
}

CreatePKCS7: function (containerName, subject)
{
    // PKCS #7 certificate request
    var objPKCS7Request = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509CertificateRequestPkcs7");

    // initialize PKCS #7 certificate request by PKCS #10 certificate request
    objPKCS7Request.InitializeFromInnerRequest(this.ComposePKCS10Request(containerName, subject));

    var objSignerCert = _certEnrollClassFactory.CreateObject("X509Enrollment.CSignerCertificate");
    var verifyType = 4; /* VerifyAllowUI, see typedef enum X509PrivateKeyVerify */
    var encodingType = 0x3; /* see typedef enum EncodingType */

    /**********************************************************************/
    /* I have to provide certificate here??? How can I obtain it from UI? */
    /**********************************************************************/
    var strCertificate = '?????????????????????';

    objSignerCert.Initialize(false, verifyType, encodingType, strCertificate);

    /*****************************************************************************/
    /* Also I'm not shure that SignerCertificate can be accessed via javascript. */
    /*****************************************************************************/
    objPKCS7Request.SignerCertificate = objSignerCert;

    // represents the top level object and enables you to enroll in a certificate hierarchy and install a certificate response
    var objEnroll = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509Enrollment");

    // Enroll
    objEnroll.InitializeFromRequest(objPKCS7Request);

    var pkcs7;

    try
    {
        pkcs7 = objEnroll.CreateRequest(XCN_CRYPT_STRING_BASE64REQUESTHEADER);
    }
    catch (e)
    {
        ...
    }

    return pkcs7;
}

有没有办法用 javascript 创建 PKCS#7 消息?

更新:我已经收到了 PKCS#10 证书请求(请参阅代码示例中的第一个函数)并且需要为其创建 PKCS#7 签名消息。好的,我解释一下我的问题。如何使用 javascript 创建签名的 PKCS#7 消息?(理想情况下,它应该允许使用 UI 指定适当的证书。)

至于 javascript,我知道这不是方便的方式,但很合适,因为我必须在客户端(在浏览器中)处理它。此外,cert注册IX509CertificateRequestPkcs7接口有标记为[WebEnabled]的方法,所以我相信一定有办法做到我所说的。

4

3 回答 3

3

您可以使用 Forge 在纯 JS 中执行 PKCS#7 和 PKCS#10(在浏览器或 node.js 中工作):

https://github.com/digitalbazaar/forge#pkcs7

https://github.com/digitalbazaar/forge#pkcs10

于 2013-07-12T01:24:47.900 回答
1

PKCS#7 格式在rfc2315中使用 ASN.1 表示法定义。

ASN.1

ASN.1 是一种用于定义数据结构的符号。此外,还有 DER - 定义如何将 ASN.1 表示法编码为二进制数据的编码规则。

ASN1.js 编码器

所以问题归结为“是否有任何实现 ASN1 的 JS 库?” 有一些解码库

但唯一的编码器是这个:https ://github.com/indutny/asn1.js

ContentInfo 和类型

让我们更深入地了解。PKCS#7 的 ASN.1 如下:

ContentInfo ::= SEQUENCE {
  contentType ContentType,
  content
    [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }

ContentType ::= OBJECT IDENTIFIER

这里我们需要了解什么是OBJECT IDENTIFIER。它是定义某种类型的特殊数字序列。PKCS#7 的有效对象标识符如下:

data OBJECT IDENTIFIER ::= { pkcs-7 1 }
signedData OBJECT IDENTIFIER ::= { pkcs-7 2 }
envelopedData OBJECT IDENTIFIER ::= { pkcs-7 3 }
signedAndEnvelopedData OBJECT IDENTIFIER ::= { pkcs-7 4 }
digestedData OBJECT IDENTIFIER ::= { pkcs-7 5 }
encryptedData OBJECT IDENTIFIER ::= { pkcs-7 6 }

pkcs-7 是 PKCS#7 格式的对象标识符。

pkcs-7 OBJECT IDENTIFIER ::=
  { iso(1) member-body(2) US(840) rsadsi(113549)
      pkcs(1) 7 }

如您所见,该字段有ANY DEFINED BY contentType修饰符。content这意味着该字段可以是 6 种类型中的任何一种,具体取决于 contentType 字段的值。

似乎您需要签名消息,因此内容类型为“signedData”

使用 JS 的 ContentInfo 编码

这如何转化为 ASN1.js 代码?你在这里:

var PKCS7_CONTENT_TYPES = {
    "1 2 840 113549 1 7 1": "data",
    "1 2 840 113549 1 7 2": "signedData",
    "1 2 840 113549 1 7 3": "envelopedData",
    "1 2 840 113549 1 7 4": "signedAndEnvelopedData",
    "1 2 840 113549 1 7 5": "digestData",
    "1 2 840 113549 1 7 6": "encryptedData",
};

var ContentInfo = asn1.define('ContentInfo', function() {
    this.seq().obj(
        this.key('contentType').objid(PKCS7_CONTENT_TYPES),
        this.key('content').optional().explicit(0).any()
    );
});

那么,如何使用这个 ContentInfo 呢?

var signedData = ...
   contentInfoEncoded = ContentInfo.encode({
     'contentType': 'signedData',
     'content': signedData
   })

签名数据

待续...

于 2014-08-07T01:59:25.533 回答
0

VBScript 方法(会提示选择证书):

Function SignMessage(Message)
    Dim oUtils
    Set oUtils = CreateObject("CAPICOM.Utilities")
    Dim cpcSigner
    Set cpcSigner = CreateObject("CAPICOM.Signer")
    cpcSigner.Options = 2 'CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY
    Dim cpcSignedData
    Set cpcSignedData = CreateObject("CAPICOM.SignedData")
    cpcSignedData.Content = oUtils.Base64Decode(Message)
    SignMessage = cpcSignedData.Sign(cpcSigner, False)
End function

Javascript 方法小心,因为在 JS 中编码字符串,你会得到损坏的签名,所以我建议使用 VBScript在你的客户端脚本中签名数据,因为它可以与 JS 互操作):

var CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY = 2;

SignMessage: function (message) {
    var cpcSigner = new ActiveXObject("CAPICOM.Signer");
    cpcSigner.Options = CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY;

    var cpcSignedData = new ActiveXObject("CAPICOM.SignedData");
    var oUtils = new ActiveXObject("CAPICOM.Utilities");
    cpcSignedData.Content = oUtils.Base64Decode(message);

    return cpcSignedData.Sign(cpcSigner, false);
}
于 2013-04-25T09:50:02.880 回答