带有 X509 证书 + 智能卡的 Web 签名

Posted

技术标签:

【中文标题】带有 X509 证书 + 智能卡的 Web 签名【英文标题】:Web signature with X509 certificate + smartcard 【发布时间】:2018-07-03 11:50:45 【问题描述】:

我正在尝试向我的网络应用添加签名功能。

签名必须使用智能卡。因此,在搜索了很多地方后,我发现 webcrypto-socket (https://peculiarventures.github.io/webcrypto-local/docs/) - Webcrypto 套接字模块实现了 Crypto 接口并使用 Fortify 应用程序进行加密实现。 p>

然后,我可以登录我的应用程序,但它生成的签名不是有效的 PKCS 签名,它只返回没有公共证书的签名哈希。

我的代码基于这个例子:https://peculiarventures.github.io/fortify-examples/example5.html

此外,欢迎使用 webcrypto-socket 和 fortify 的 cmets!

我正在做这样的事情,其中​​提供者和证书按照 fortify 的建议填写:

function sign() 
    var provider;
    var cert;
    Promise.resolve()
        .then(function () 
            var providerID = document.getElementById("providers").value;
            return ws.getCrypto(providerID)
        )
        .then(function (crypto) 
            provider = crypto;
          setEngine('subtle', crypto, 
    new CryptoEngine( name: 'subtle', crypto: crypto, subtle: crypto.subtle ));

            return GetCertificate(provider,
                document.getElementById("certificates").value);
        )
        .then(function (certificate) 
            cert = certificate;
            return GetCertificateKey("private", provider,
                document.getElementById("certificates").value);
        )
        .then(function (key) 
            if (!key) 
                throw new Error("Certificate doesn't have private key");
            

            var textToSignElement = document.getElementById("textToSign");
            var signatureResultElement = 
                document.getElementById("signatureResult");
            signDataElement(textToSignElement, signatureResultElement, 
                cert, key, provider);

        )


function signDataElement(textToSignElement, signatureResultElement, 
    key, cert, provider) 
    var message = pvtsutils.Convert.FromBase64(hashElement.value);
    var hashAlg = key.algorithm.hash.name;
    var sequence = Promise.resolve();
    sequence = sequence.then(() => provider.subtle.digest( name: hashAlg ,
        new Uint8Array(message)));
    var cmsSignedSimpl = null;
    var messHex = null;
    var certRaw = null;

    sequence = sequence.then(function (res) 
        messHex = res;
        return GetCertificateAsPEM(provider, cert);
    );

    //region Combine all signed extensions
    sequence = sequence.then(result => 
        certRaw = result;
        var signedAttr = [];

        signedAttr.push(new Attribute(
            type: "1.2.840.113549.1.9.3",
            values: [new ObjectIdentifier( value: "1.2.840.113549.1.7.1" )]
        )); // contentType

        signedAttr.push(new Attribute(
            type: "1.2.840.113549.1.9.5",
            values: [new UTCTime( valueDate: new Date() )]
        )); // signingTime

        signedAttr.push(new Attribute(
            type: "1.2.840.113549.1.9.4",
            values: [new OctetString( valueHex: messHex)]
        )); // messageDigest

        return signedAttr;
    );

    sequence = sequence.then(signedAttr => 
        var asn1 = fromBER(PemToDer(certRaw));
        var newCert = new Certificate( schema: asn1.result );

        newCert.issuer.typesAndValues.push(new AttributeTypeAndValue(
            type: "2.5.4.3", // Common name
            value: new BmpString( value: cert.issuerName )
        ));
        cmsSignedSimpl = new SignedData(
            version: 1,
            encapContentInfo: new EncapsulatedContentInfo(
                eContentType: "1.2.840.113549.1.7.1" // "data" content type
            ),
            signerInfos: [new SignerInfo(
                version: 1,
                sid: new IssuerAndSerialNumber(
                    issuer: newCert.issuer,
                    serialNumber: newCert.serialNumber
                ),
                signedAttrs: new SignedAndUnsignedAttributes(
                    type: 0,
                    attributes: signedAttr
                )
            )],
            certificates: [newCert]
        );
        return cmsSignedSimpl.sign(key, 0, hashAlg, message)
    );

    sequence = sequence.then(() => 
        var cmsSignedSchema = cmsSignedSimpl.toSchema(true);

        var cmsContentSimp = new ContentInfo(
            contentType: "1.2.840.113549.1.7.2",
            content: cmsSignedSchema
        );

        var _cmsSignedSchema = cmsContentSimp.toSchema();

        //region Make length of some elements in "indefinite form"
        _cmsSignedSchema.lenBlock.isIndefiniteForm = true;

        var block1 = _cmsSignedSchema.valueBlock.value[1];
        block1.lenBlock.isIndefiniteForm = true;

        var block2 = block1.valueBlock.value[0];
        block2.lenBlock.isIndefiniteForm = true;

        //endregion

       return _cmsSignedSchema.toBER(false);
    , error => Promise.reject(`Erorr during signing of CMS Signed Data: $error`));

    sequence.then((certificateBuffer) => 
        var certSimplString = String.fromCharCode.apply(null, 
            new Uint8Array(certificateBuffer));

        signatureElement.value = formatPEM(window.btoa(certSimplString));

        alert("Certificate created successfully!");
    )

    return sequence;


function GetCertificate(provider, certID) 
    var certID;
    return provider.certStorage.getItem(certID)
        .then(function (cert) 
            return cert;
        );


function GetCertificateAsPEM(provider, cert) 
    return provider.certStorage.exportCert('PEM', cert);

signDataElement 是基于https://pkijs.org/examples/CMSSigned_complex_example.html

【问题讨论】:

【参考方案1】:

我们在 Hancock (https://hancock.ink) 中使用 Fortify (https://fortifyapp.com) 与 PKIjs (https://pkijs.org) 和 CAdESjs (https://github.com/PeculiarVentures/CAdES.js) 来创建 CMS 签名。

您可以在此处查看基于 PKIjs 的 JS 中的基本 CMS 示例:https://pkijs.org/examples/CMSSigned_complex_example.html

您需要向我们提供您正在做什么的示例,我们可以告诉您您需要做哪些不同的事情。

瑞恩

【讨论】:

瑞恩,感谢您的回答。我添加了一些我用来签名的代码。我正在查看您提到的示例,但我无法实现的是获得 base 64 中的证书.. Nacho,您似乎根本没有尝试过使用上面链接的 CMS 示例。如果您遇到麻烦,很乐意提供帮助,但我没有时间为您做这件事。 Ryan,我已经阅读了示例,如果我没记错的话,我需要私钥和公钥或 PEM 证书。我尝试过不同的方法,但我做不到:( 您不需要私钥,WebCrypto 会为您抽象出来。由于您没有提供任何代码来尝试执行您的请求,而无需简单地为您编写代码,因此我无能为力。在您之前的回复中,您询问了如何获取证书,不清楚您的应用程序流程是什么样的,但如果您需要枚举证书,您可以查看以下示例:peculiarventures.github.io/fortify-examples/example4.html Ryan,我已经编辑了答案并添加了更多代码。我希望它更清楚

以上是关于带有 X509 证书 + 智能卡的 Web 签名的主要内容,如果未能解决你的问题,请参考以下文章

无法在 python 中使用 X509Certificate 对数据进行签名

如何创建一个自签名的SSL证书(X509)

是否可以创建没有签名字段的 x509 证书? [复制]

CA签名X509证书包含X509v3扩展名“主题备用名称”两次

如何验证 x509 证书的签名?

使用 X509 证书进行消息签名