Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get certificate signature #107

Closed
LumaRay opened this issue Apr 15, 2019 · 20 comments
Closed

Get certificate signature #107

LumaRay opened this issue Apr 15, 2019 · 20 comments

Comments

@LumaRay
Copy link

LumaRay commented Apr 15, 2019

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?

@microshine
Copy link
Contributor

You need to use ASN.1 parser. You can use PKIjs module for it

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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 :)

@microshine
Copy link
Contributor

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.

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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);
}

@microshine
Copy link
Contributor

I think the problem is in data type. cert.value is Buffer, but fromBER requires ArrayBuffer

Try new Uint8Array(cert.value).buffer

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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.

@microshine
Copy link
Contributor

It would be nice to implement GOST algorithms in WebCrypto API. It'd allow to use GOST for CMS, XMLDSig, XAdES, JOSE creation

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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.

@microshine
Copy link
Contributor

To implement GOST we need:

  • Add GOST params and mechanisms to pkcs11js
  • Wrap GOST params in graphene-pk11
  • Add GOST crypto to node-webcrypto-p11

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

Well, lots of work, but... hell it's useful!

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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)

@YuryStrozhevsky
Copy link

Try to debug yourself. The valueHex has ArrayBuffer type, not string with hex representation.

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

Thank you Yury, sorry, I was mislead by that "Hex" affix.

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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...

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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?

@microshine
Copy link
Contributor

microshine commented Apr 15, 2019

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"}),
],

https://github.com/PeculiarVentures/PKI.js/blob/d967c765f01c08914df626a4fe7156e56e7ef80f/src/SignedData.js#L984

@LumaRay
Copy link
Author

LumaRay commented Apr 15, 2019

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()}),
],

@LumaRay
Copy link
Author

LumaRay commented Apr 16, 2019

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.

@microshine
Copy link
Contributor

@LumaRay Please call me on Skype:microshine82

@LumaRay
Copy link
Author

LumaRay commented Apr 17, 2019

Thank you Stepan! That was a really fruitful help!!
Finally we got that thing working :)
I've attached a working code sample.

code.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants