带有 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 对数据进行签名