使用 X509 证书 + 智能卡的 Web 签名
Web signature with X509 certificate + smartcard
我正在尝试向我的网络应用程序添加签名功能。
必须使用智能卡签名。所以,在很多地方搜索之后,我发现 webcrypto-socket (https://peculiarventures.github.io/webcrypto-local/docs/) - Webcrypto 套接字模块实现了 Crypto 接口并使用 Fortify 加密实施申请。
然后,我可以登录到我的应用程序,但它生成的签名不是有效的 PKCS 签名,它只返回没有 public 证书的签名哈希。
我的代码基于这个例子:https://peculiarventures.github.io/fortify-examples/example5.html
此外,欢迎对 webcrypto-socket 和 fortify 发表评论!
我正在做这样的事情,其中提供者和证书按照 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
我们使用 Fortify (https://fortifyapp.com) in Hancock (https://hancock.ink) with PKIjs (https://pkijs.org) and CAdESjs (https://github.com/PeculiarVentures/CAdES.js) 创建 CMS 签名。
您可以在此处查看基于 PKIjs 的 JS 中的基本 CMS 示例:https://pkijs.org/examples/CMSSigned_complex_example.html
您需要向我们提供您正在做的事情的示例,我们可以告诉您您需要做哪些不同的事情。
瑞安
我正在尝试向我的网络应用程序添加签名功能。
必须使用智能卡签名。所以,在很多地方搜索之后,我发现 webcrypto-socket (https://peculiarventures.github.io/webcrypto-local/docs/) - Webcrypto 套接字模块实现了 Crypto 接口并使用 Fortify 加密实施申请。
然后,我可以登录到我的应用程序,但它生成的签名不是有效的 PKCS 签名,它只返回没有 public 证书的签名哈希。
我的代码基于这个例子:https://peculiarventures.github.io/fortify-examples/example5.html
此外,欢迎对 webcrypto-socket 和 fortify 发表评论!
我正在做这样的事情,其中提供者和证书按照 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
我们使用 Fortify (https://fortifyapp.com) in Hancock (https://hancock.ink) with PKIjs (https://pkijs.org) and CAdESjs (https://github.com/PeculiarVentures/CAdES.js) 创建 CMS 签名。
您可以在此处查看基于 PKIjs 的 JS 中的基本 CMS 示例:https://pkijs.org/examples/CMSSigned_complex_example.html
您需要向我们提供您正在做的事情的示例,我们可以告诉您您需要做哪些不同的事情。
瑞安