Closed LumaRay closed 5 years ago
You need to use ASN.1 parser. You can use PKIjs module for it
Ho do I work with PKCS#11 hardware tokens from PKIjs? I cannot find it among the examples. I need to export CMS detached signature using GOST algorythm, Yury has suggested this example: https://github.com/PeculiarVentures/PKI.js/tree/master/examples/CMSSignedComplexExample But I need to modify it to use certificate from a token, and I need to sign CMS by a token using Kryptoki interface. Need examples badly :)
PKIjs supports Crypto Engines. You can create your own engine which will implement GOST algorithms.
You can use this code for example. It implements unsupported WebCrypto algorithms like RC2 an DES.
Thanks, I know that - Yury Strozhevsky has told me about writing that engine... but writing an engine is a comples task, I'd rather assemble ASN.1 structure of CMS and save it as a DER .sig file, all I need for it is a certificate from a token, a message digest and then sign the CMS.
I try this way, "cert" is imported, but the last line fails, why?
var graphene = require("graphene-pk11");
var asn1js = require("asn1js");
var Module = graphene.Module;
var mod = Module.load("C://Windows/System32/rtPKCS11ECP.dll", "Rutoken");
var slot = mod.getSlots(0);
if (slot.flags & graphene.SlotFlag.TOKEN_PRESENT) {
var session = slot.open(graphene.SessionFlag.SERIAL_SESSION | graphene.SessionFlag.RW_SESSION);
session.login("12345678");
const certificates = session.find({ class: graphene.ObjectClass.CERTIFICATE });
const cert = certificates.items(0).toType();
const asn1 = asn1js.fromBER(cert.value);
}
I think the problem is in data type. cert.value
is Buffer
, but fromBER requires ArrayBuffer
Try new Uint8Array(cert.value).buffer
It worked, thanks! Now I stepped little further :) Actually Rutoken't Cryptoki library has few extended fuctions that perform that type of PKCS#7 signing/verification https://dev.rutoken.ru/pages/viewpage.action?pageId=3178555#id-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B9%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D1%8F-C_EX_PKCS7Sign()
But I need a universal solution that would work with other HSMs as well.
It would be nice to implement GOST algorithms in WebCrypto API. It'd allow to use GOST for CMS, XMLDSig, XAdES, JOSE creation
Well, from what I've found, there are few javascript implementations: http://gostcrypto.com/ https://github.com/rudonick/crypto But those are not using HSMs' internal crypto providers.
To implement GOST we need:
pkcs11js
graphene-pk11
node-webcrypto-p11
Well, lots of work, but... hell it's useful!
Strange things happen... When this code is executed:
const signedAttr = [];
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.3",// contentType (PKCS #9)
values: [
new asn1js.ObjectIdentifier({ value: "1.2.840.113549.1.7.1" })// data (PKCS #7)
]
})); // contentType
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.5",// signingTime (PKCS #9)
values: [
new asn1js.UTCTime({ valueDate: new Date() })
]
})); // signingTime
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.4",// messageDigest (PKCS #9)
values: [
new asn1js.OctetString({ valueHex: signature.toString("hex") })/////////////////////
]
})); // messageDigest
cmsSignedSimpl.signerInfos[0].signedAttrs = new SignedAndUnsignedAttributes({
type: 0,
attributes: signedAttr//result// signedAttr
});
console.log(cmsSignedSimpl.signerInfos[0].signedAttrs);
data = cmsSignedSimpl.signerInfos[0].signedAttrs.toSchema(true).toBER(false);
It faild with this error:
SignedAndUnsignedAttributes {
type: 0,
attributes:
[ Attribute { type: '1.2.840.113549.1.9.3', values: [Array] },
Attribute { type: '1.2.840.113549.1.9.5', values: [Array] },
Attribute { type: '1.2.840.113549.1.9.4', values: [Array] } ],
encodedValue: ArrayBuffer { byteLength: 0 } }
App threw an error during load
RangeError: Source is too large
at Uint8Array.set (<anonymous>)
at utilConcatBuf (d:\_One\electron-test\node_modules\pvutils\build\utils.js:270:12)
at OctetString.toBER (d:\_One\electron-test\node_modules\asn1js\build\asn1.js:850:39)
at LocalConstructedValueBlock.toBER (d:\_One\electron-test\node_modules\PKIjs\node_modules\asn1js\build\asn1.js:1109:35)
at Set.toBER (d:\_One\electron-test\node_modules\PKIjs\node_modules\asn1js\build\asn1.js:848:59)
at LocalConstructedValueBlock.toBER (d:\_One\electron-test\node_modules\PKIjs\node_modules\asn1js\build\asn1.js:1109:35)
at Sequence.toBER (d:\_One\electron-test\node_modules\PKIjs\node_modules\asn1js\build\asn1.js:848:59)
at LocalConstructedValueBlock.toBER (d:\_One\electron-test\node_modules\PKIjs\node_modules\asn1js\build\asn1.js:1109:35)
at Constructed.toBER (d:\_One\electron-test\node_modules\PKIjs\node_modules\asn1js\build\asn1.js:848:59)
at Object.<anonymous> (d:\_One\electron-test\app.js:517:66)
Try to debug yourself. The valueHex has ArrayBuffer type, not string with hex representation.
Thank you Yury, sorry, I was mislead by that "Hex" affix.
Sorry guys for bothering you again. I need to set this in ASN.1:
SET (1 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.643.7.1.1.2.2 gost2012Digest256 (GOST R 34.11-2012 256 bit digest)
NULL
I add it this way:
cmsSignedSimpl = new SignedData({
version: 1,
digestInfo: new DigestInfo({
digestAlgorithm: new AlgorithmIdentifier({algorithmId: "1.2.643.7.1.1.2.2"})
}),
encapContentInfo: new EncapsulatedContentInfo({
eContentType: "1.2.840.113549.1.7.1" // "data" content type
}),
signerInfos: [
new SignerInfo({
version: 1,
sid: new IssuerAndSerialNumber({
// issuer: certSimpl.issuer,
// serialNumber: certSimpl.serialNumber
issuer: certificate.issuer,
serialNumber: certificate.serialNumber
})
})
],
//certificates: [certSimpl]
certificates: [certificate]
});
But it is not added...
This worked:
digestAlgorithms: [new DigestInfo({
digestAlgorithm: new AlgorithmIdentifier({algorithmId: "1.2.643.7.1.1.2.2"})
})],
But it gives this instead:
SET (1 elem)
SEQUENCE (2 elem)
SEQUENCE (1 elem)
OBJECT IDENTIFIER 1.2.643.7.1.1.2.2 gost2012Digest256 (GOST R 34.11-2012 256 bit digest)
OCTET STRING (0 elem)
Is this the same?
https://tools.ietf.org/html/rfc5652
SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
signerInfos SignerInfos }
DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
DigestAlgorithmIdentifier ::= AlgorithmIdentifier
Try
digestAlgorithms: [
new AlgorithmIdentifier({algorithmId: "1.2.643.7.1.1.2.2"}),
],
Try
Thanks again! I added like this to get that "NULL" parameter as well:
digestAlgorithms: [
new AlgorithmIdentifier({algorithmId: "1.2.643.7.1.1.2.2", algorithmParams: new asn1js.Null()}),
],
Strange thing is that using your library I get this format: https://www.screencast.com/t/qbfriw8pQTl With this code:
cmsSignedSimpl = new SignedData({
version: 1,
digestAlgorithms: [
new AlgorithmIdentifier({algorithmId: "1.2.643.7.1.1.2.2", algorithmParams: new asn1js.Null()}),
],
encapContentInfo: new EncapsulatedContentInfo({
eContentType: "1.2.840.113549.1.7.1" // "data" content type
}),
signerInfos: [
new SignerInfo({
version: 1,
sid: new IssuerAndSerialNumber({
issuer: certificate.issuer,
serialNumber: certificate.serialNumber
}),
digestAlgorithm: new AlgorithmIdentifier({algorithmId: "1.2.643.7.1.1.2.2", algorithmParams: new asn1js.Null()}),
signature: new asn1js.OctetString({ valueHex: signature })
})
],
certificates: [certificate]
});
But when I create a signature using Kontur Signature service which uses Crypto PRO provider and another token, which is accepted by our government portal Gosuslugi, it's structure is different: https://www.screencast.com/t/FhczQl2P And I just don't understand how to put that public key entry instead of a signature in there.
@LumaRay Please call me on Skype:microshine82
Hello. How can I get a signature value from a certificate stored on a PKCS#11 hardware token like Rutoken ECP 2.0 using this Graphene library?