Closed oliveryepez closed 5 years ago
policies
option is not implemented in current version of xadesjs
https://github.com/PeculiarVentures/xadesjs/blob/master/index.d.ts#L24
But you can set you own values before Sign
function calling
Here is example of signing XML document and adding XADES properties like SigningTime, SigningCertificate, SignerRole via options and SignaturePolicyIdentifier programmatically
//@ts-check
const asn1js = require("asn1js");
const pkijs = require("pkijs");
const xades = require("xadesjs");
const xmldsig = require("xmldsigjs");
const CryptoOSSL = require("node-webcrypto-ossl");
const crypto = new CryptoOSSL();
const commonName = "Test self-signed certificate";
const alg = {
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256" },
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 2048,
};
async function CreateCertificate(commonName, keys, alg) {
// Generate new certificate
const certificate = new pkijs.Certificate();
certificate.version = 2;
certificate.serialNumber = new asn1js.Integer({ value: 1 });
certificate.issuer.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.6", // Country name
value: new asn1js.PrintableString({ value: "EN" })
}));
certificate.issuer.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.3", // Common name
value: new asn1js.BmpString({ value: commonName })
}));
certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.6", // Country name
value: new asn1js.PrintableString({ value: "EN" })
}));
certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
type: "2.5.4.3", // Common name
value: new asn1js.BmpString({ value: commonName })
}));
certificate.notBefore.value = new Date();
certificate.notAfter.value = new Date();
certificate.notAfter.value.setFullYear(certificate.notAfter.value.getFullYear() + 1);
certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array
await certificate.subjectPublicKeyInfo.importKey(keys.publicKey);
await certificate.sign(keys.privateKey, alg.hash.name);
// Convert certificate to DER
const derCert = certificate.toSchema(true).toBER(false);
// const pem = DerToPem(derCert, "CERTIFICATE");
const pem = Buffer.from(derCert).toString("base64");
console.log(pem);
// import key to crypto
return pem;
}
async function GenerateKeys(alg) {
return await crypto.subtle.generateKey(alg, false, ["sign", "verify"]);
}
async function main() {
// set crypto engine
xades.Application.setEngine("OpenSSL", crypto);
pkijs.setEngine("OpenSSL", crypto, new pkijs.CryptoEngine({ name: "OpenSSL", crypto, subtle: crypto.subtle }));
const keys = await GenerateKeys(alg);
const cert = await CreateCertificate(commonName, keys, alg);
var xmlString = '<player bats="left" id="10012" throws="right">\n\t<!-- Here\'s a comment -->\n\t<name>Alfonso Soriano</name>\n\t<position>2B</position>\n\t<team>New York Yankees</team>\n</player>';
var xmlDoc = xades.Parse(xmlString);
const xml = new xades.SignedXml(xmlDoc);
// If you need custom data you can add it manually
xml.SignedProperties.SignedSignatureProperties.SignaturePolicyIdentifier.SignaturePolicyId.SigPolicyId.Identifier.Qualifier = "OIDAsURI";
xml.SignedProperties.SignedSignatureProperties.SignaturePolicyIdentifier.SignaturePolicyId.SigPolicyId.Identifier.Value = "my.uti.oid";
xml.SignedProperties.SignedSignatureProperties.SignaturePolicyIdentifier.SignaturePolicyId.SigPolicyHash.DigestMethod.Algorithm = "SHA-1";
xml.SignedProperties.SignedSignatureProperties.SignaturePolicyIdentifier.SignaturePolicyId.SigPolicyHash.DigestValue = new Uint8Array(20);
const signedXml = await xml.Sign( // Signing document
alg, // algorithm
keys.privateKey, // key
xmlDoc, // document
{ // options
keyValue: keys.publicKey,
x509: [cert],
signingCertificate: cert,
references: [
{ hash: "SHA-256", transforms: ["enveloped"] }
],
productionPlace: {
country: "Country",
state: "State",
city: "City",
code: "Code",
},
signerRole: {
claimed: ["Some role"]
}
}
);
console.log(signedXml.toString());
}
main()
.catch((err) => {
console.log(err);
})
Hi @microshine thanks for your quick reponse, Damn! that was fast!!!... I got another question and I hope you can help me... I got and p12 crypto key and I need to sign and xml just the way you showed me on the last comment, but How can I import the cert and key pairs for do that?... I opened the crypto and it have at least 3 PEM certs.
Thanks in advance for your cooperation
Regards!
@oliveryepez You can use PKIjs for PKCS#12. Here is example of it https://pkijs.org/examples/PKCS12SimpleExample.html
@oliveryepez see unmitigatedrisk.com/?p=543 for some details on PKCS#12 in the browser using PKIjs. We have since made some improvements that allow the use of the weaker cryptographic constructs but currently, it only works in Node where those algorithms are available.
Thank you guys, for your responses, I'm compelled to use this type of keys @rmhrisk because is the key that I have for sign an xml with XADES-EPES signature, but I don't need to do this in browser, can be a simple js file running with node, this package was the only package that I found for do this type of signatures.
I follow you example @microshine but a think I do something wrong because I'm trying to parse the key like this.
let file_buffered = fs.readFileSync(filepath);
const password_buffered = pvutils.stringToArrayBuffer(password);
const asn1 = asn1js.fromBER(file_buffered);
const pkcs12 = new pkijs.PFX({schema: asn1.result});
And i got the following error (node:22078) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Object's schema was not verified against input data for PFX
What I'm doing wrong, I'm trying to get X509 Certificate and Public key for create XADES-EPES signature with xadesjs
Thank you for all your help guys
@oliveryepez take a look at this example. https://github.com/PeculiarVentures/PKI.js/tree/master/examples/NodePKCS12Example
It will be easier to support PKIjs issues in the PKIjs repository.
Please post your final solution here for others but move discussions to PKCS#12/PFX support to that repository.
I think the problem is here
let file_buffered = fs.readFileSync(filepath);
ASN1js and PKIjs work with ArrayBuffer
. fs.readFileSync
returns Buffer
. You need to convert Buffer to ArrayBuffer.
let file_buffered = new Uint8Array(fs.readFileSync(filepath)).buffer;
NOTE: You must be sure that PFX has DER format and you use BINARY Buffer, otherwise you must to convert PEB to DER
https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx
\n
, \r
@oliveryepez Were you able to create a Xades-EPES signature? Can you post an example please?
Hey @oliveryepez any updates on this? Could you figure out what you where looking for?
Hi @oliveryepez, do you create Xades-EPES with this solution?
@oliveryepez did you find a solution?
XMLDSIG
|
<ds:Signature ID?>- - - - - - - - -+- - - - -+
<ds:SignedInfo> | |
<ds:CanonicalizationMethod/> | |
<ds:SignatureMethod/> | |
(<ds:Reference URI? > | |
(<ds:Transforms>)? | |
<ds:DigestMethod/> | |
<ds:DigestValue/> | |
</ds:Reference>)+ | |
</ds:SignedInfo> | |
<ds:SignatureValue/> | |
(<ds:KeyInfo>)?- - - - - - - - - + |
|
<ds:Object> |
|
<QualifyingProperties> |
|
<SignedProperties> |
|
|
<SignedSignatureProperties> |
(SigningTime)? |
(SigningCertificate)? |
(SignaturePolicyIdentifier) |
(SignatureProductionPlace)? |
(SignerRole)? |
</SignedSignatureProperties> |
|
<SignedDataObjectProperties> |
(DataObjectFormat)* |
(CommitmentTypeIndication)* |
(AllDataObjectsTimeStamp)* |
(IndividualDataObjectsTimeStamp)* |
</SignedDataObjectProperties> |
|
</SignedProperties> |
|
<UnsignedProperties> |
|
<UnsignedSignatureProperties> |
(CounterSignature)* |
</UnsignedSignatureProperties> |
|
</UnsignedProperties> |
|
</QualifyingProperties> |
|
</ds:Object> |
|
</ds:Signature>- - - - - - - - - - - - - - - +
|
XAdES-EPES
xadesjs
module allows has simple API to create Signature with a list of SignedSignatureProperties
via Sign method with Options
You can find type definition for Options here
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
https://gist.github.com/microshine/f853759219452d4d397e38b972eaee78
<Test><Document attr="Hello"/><ds:Signature Id="id-62d6abd24e1c" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>tg2dxbUKpoX43m9Unsu0gPiXIbJXtS54EZWpWznQigE=</ds:DigestValue></ds:Reference><ds:Reference URI="#xades-id-62d6abd24e1c" Type="http://uri.etsi.org/01903#SignedProperties"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>oL93BXgu5sd730AZ7aGTHriHlDzcnLNUqWpeasWjz/w=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>ct4W+J8u9MUrRKgalGQtq7iH85gXiOVAl+Bk/Nj3jn29zQc1U7Mn5axf+tryLO34VpwQArf7lwY8H/TanepG2ya3c+4nCs/wZThnggh/IyZ14wJECYYP5bdQqelTu0asCTJPpomx5CcWNbadAKPKqZ0bzFA7Zjlzny7TFbTLHtZJ+sfVZPKI0ik1rdy8rHSZdAdx2sXoRGme7Iki7TNDPJO9/1pyTGoABWpyGA7eBKyN0kdHYaq1VcBCSbICMzxBZCmwOW+VgMRX5Mvwe7V/QOB2gjkYQmLuGeViBgaU1v9y7Qx3Ts8tJKono4uQSRQbSxJ+Fd6KBVL4gJudBlUZXp1Dfl+TCxujwqez4tY+T+JnztRCQE8tLrciuvMxUbpLfpyE1Co8TOEca9PCkNrYhx63z8aW8v8zMIHWp7hAEl7den2KokN7vYof0oaV5vfePCSFN0v1QwK5BM1m0lSO324IaH1QZl0z+81x8KVztNgl8vPrHR/gN0nYDZLmm+a7Ff6k1yBuTicdj26a5S/S20jGKqDnnnHbqlOw0ug90tYwup8SbfIrYZxe86EwAWGppJJrjetQurGVd8Fjh7JiZ4iFwHWK5YAuzDHyLTXuzopwVA3XcAcAfT0350MuVWdOlKzg1bMxsceX+wJkGNoYHXpBr4qMLfFn2rxpcS6q6eM=</ds:SignatureValue><ds:Object><xades:QualifyingProperties Target="#id-62d6abd24e1c" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"><xades:SignedProperties Id="xades-id-62d6abd24e1c"><xades:SignedSignatureProperties><xades:SigningTime>2018-01-09T14:00:54.006Z</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>bu+t1r/OsLb0uLiKhFHDQvO/P2WlzLW1td48ji/qeM0=</ds:DigestValue></xades:CertDigest><xades:IssuerSerial><ds:X509IssuerName>C=RU, ST=Marj El, L=Yoshkar-Ola, O=PeculiarVentures, CN=microshine, E=microshine@mail.ru</ds:X509IssuerName><ds:X509SerialNumber>12630331543579879860</ds:X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate><xades:SignaturePolicyIdentifier><xades:SignaturePolicyId><xades:SigPolicyId><xades:Identifier Qualifier="OIDAsURI">quilifier.uri</xades:Identifier></xades:SigPolicyId><xades:SigPolicyHash><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>Ilnj+FSn0X9MCRXFGIkHkMozR2P2rrS3UruywJVCBEg=</ds:DigestValue></xades:SigPolicyHash><xades:SigPolicyQualifiers><xades:SigPolicyQualifier><xades:SPUserNotice><xades:NoticeRef><xades:Organization>PeculiarVentures</xades:Organization><xades:IntegerList><xades:int>1</xades:int><xades:int>2</xades:int><xades:int>3</xades:int><xades:int>4</xades:int><xades:int>5</xades:int></xades:IntegerList></xades:NoticeRef></xades:SPUserNotice></xades:SigPolicyQualifier></xades:SigPolicyQualifiers></xades:SignaturePolicyId></xades:SignaturePolicyIdentifier><xades:SignatureProductionPlace><xades:City>Yoshkar-Ola</xades:City><xades:StateOrProvince>Marij El</xades:StateOrProvince><xades:PostalCode>424000</xades:PostalCode><xades:CountryName>Russia</xades:CountryName></xades:SignatureProductionPlace></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object></ds:Signature></Test>
xmlsec1 verify --pubkey-cert-pem cert.pem sig.xml
output
OK
SignedInfo References (ok/all): 2/2
Manifests References (ok/all): 0/0
Thank you @microshine the solution is working, for those who have a .p12 file first need to extract into two separated files.
Extract public and private key from pkcs12 file
After extracting your private key and cert you need to decrypt the private key for usage Then you need to delete the headers generated by that separations in public cert.
Delete all before -----BEGIN CERTIFICATE-----
So in cert.pem in line 31, you need to put the cert without the header (step above) and in line 35 you need to put the Unencrypted RSA key
In my personal case I require some modifications to the @microshine gist, specifically on line 78
Replace:
xml.firstChild.appendChild(signature.GetXml());
With:
xml.documentElement.appendChild(signature.GetXml());
NOTE: This is the first time that I work with p12 files, specifically with "Ministerio de Hacienda Costa Rica" digital sign so I don't know actually if those steps are required with other p12 files.
@variux you can use PKCS#12 also, see - https://github.com/PeculiarVentures/PKI.js/blob/7b953ee08ee342d085328ec02152c087dae74917/examples/NodePKCS12Example/es6.js
Thank you @rmhrisk I will try using PKI.js, will be useful for me!
Hi @microshine when I use xmlsec1 command it returns me a "Invalid data: data and digest do not match" I think that is a wrong calculated digest but I don't know why, also I require the X509Data and isn't in my xml
Did you edit the file after the signature was applied?
No, It was not edited, I don't know if deleting the headers of cert.pem and key.pem could change the results, but I sign the document without the headers, also I wrote the string to an xml file, I don't know if its also affects
@variux For X509Data you need to add x509
option
const signature = await xadesXml.Sign( // Signing document
alg, // algorithm
key, // key
xml, // document
{ // options
references: [
{ hash, transforms: ["c14n", "enveloped"] }
],
x509: [x509],
policy: {
@variux Could you share your signed xml and cert.pem? Email: microshine@mail.ru
@microshine sure, thank you, has been sent, I added the x509 data
@variux could you sign xml one more time?
Add console.log("Hash:\n%s\n", xml);
before node_modules/xmldsigjs/dist/index.js:550
return Promise.resolve().then(function () {
var buf;
if (typeof xml === "string") {
console.log("Hash:\n%s\n", xml);
buf = XmlCore.Convert.FromString(xml, "utf8");
Run your script Send me Hash log
Sent to your email
@variux Do you have the lates version of xadesjs, xmldsigjs, and xml-core?
Is see difference in xml canonicalization. I fixed some issues in xmldsigjs
Can you run npm update
and sign again?
xadesjs@2.0.11
xmldsigjs@2.0.18
xml-core@1.0.12
Yeah, I'm using the latest versions
https://gist.github.com/variux/8044b9ceb2896facd88d09241b12393b
This is my code if you want to check it
@variux thank you I'll do some tests tomorrow. It can be XML Canonicalization bug
@microshine thanks to you for the help, I'll be waiting for your test
@variux I found problem. I need time to fix it. I'll notify you when it's done
Thank you @microshine I'm gonna be waiting for that!
@variux I published new version of xmldsigjs@2.0.20 Can you check it?
Thank you thank you thank you @microshine, that was really fast, it's working!
I have some questions, I need to established more nodes, that I didn't see at Options, specifically SignedDataObjectProperties, I can access them but by other ways but I don't know really how to configure them and set the DataCollectionItems
Also I need the modulus node, the exponent node
@variux You can use xadesjs API for it
Add this code before Sign
method
const dataFormat = new xadesjs.xml.DataObjectFormat();
dataFormat.ObjectReference = "ojbRef";
dataFormat.Description = "description";
dataFormat.MimeType = "mime-type";
dataFormat.Encoding = "enc";
xadesXml.SignedProperties.SignedDataObjectProperties.DataObjectFormats.Add(dataFormat);
xades:SignedDataObjectProperties><xades:DataObjectFormat ObjectReference="ojbRef"><xades:Description>description</xades:Description><xades:MimeType>mime-type</xades:MimeType><xades:Encoding>enc</xades:Encoding></xades:DataObjectFormat></xades:SignedDataObjectProperties>
Do you use xadesjs type definition? https://github.com/PeculiarVentures/xadesjs/blob/master/index.d.ts
If you need KeyValue
in Signature
you need to parse signing Certificate
(PKIjs) and export public key from it. Then you can add public key to signing options like keyValue
Example with keyValue
Thank you @microshine, can the reference and keyinfo have and id and URI?
Examples:
<ds:Reference Id="xmldsig-5aa63746-c7f0-4627-a0c6-109043e3a003-ref0" URI="">
<ds:Reference URI="#xmldsig-5aa63746-c7f0-4627-a0c6-109043e3a003-keyinfo">
<ds:KeyInfo Id="xmldsig-5aa63746-c7f0-4627-a0c6-109043e3a003-keyinfo">
Here's an example that I need to follow and the why I'm looking for those options, this is signed with xades4j in Java
Reference option supports id
and uri
https://github.com/PeculiarVentures/xmldsigjs/blob/master/index.d.ts#L971
But you cannot add Id for KeyInfo from options. You need to do it from script
Source code for KeyInfo adding https://github.com/PeculiarVentures/xmldsigjs/blob/master/src/signed_xml.ts#L494-L501
You just need to add
keyValue.Id = "xmldsig-5aa63746-c7f0-4627-a0c6-109043e3a003-keyinfo";
NOTE: all Signature objects modifications must be completed before Sign method
Thank you @microshine for all the help given, my software is working now, last question, which IDE are you using?
Hi @variux. I am also trying to sign an XML document with Xades-Epes according to the requirements established by the Ministry of Finance of Costa Rica.
When I send the signed file to the DGT, the document is rejected due to problems with the signature.
From the code in https://gist.github.com/variux/8044b9ceb2896facd88d09241b12393b I try to generate the signed XML, however I have a problem with the calculation of the digest.
When validating the XML in https://xadesjs.com/ I get the error "XMLJS0013: Cryptographic error: Invalid digest for uri ''. Calculated digest is a / F0 / Dnhn6EUANbmeyrzXy84sxR / Rr7aj7aQ9zvnliE = but the xml to validate supplies digest cft9G9yayijVVdg3ilM3sIYqu1l67IU / jl0isrrLwCM = ".
Could you share your final code after you applied the suggestions from @microshine and @rmhrisk?
These are the versions of the npm packages that I am working with:
xadesjs@2.0.11 xmldsigjs@2.0.20 xml-core@1.0.12
@charlienux can you provide us a working and broken document based on the same origin document so we can help.
@rmhrisk Thank you very much for your help.
I attach a zip file with the documents. sign.zip
Hi @charlienux some people are working on a complete SDK for Ministry of Finance in Costa Rica using this library, search at Facebook for crlibre.org
I didn't get any errors but when I sent to Ministry Of Finance they reject the document sign due to an incorrect reference.
This is what the technical personnel of Finance Ministry told me
"The reference to the data to sign is incorrectly referenced"
But I have been bussy and I have not had time to check what is.
@microshine when you have time please take a look at @charlienux's samples
@variux @charlienux you may want to show your Ministry Of Finance https://fortifyapp.com also
@charlienux I ran your JS script from ZIP and validated signed xml with xmlsec1
application. It has valid signature.
> xmlsec1 --verify signedDocument2.xml
OK
SignedInfo References (ok/all): 2/2
Manifests References (ok/all): 0/0
You have error on xadesjs.com because it had old JS library. I updated it
I published new version v2.0.12. It has the latest working Web version
Could someone help me with this
"The reference to the data to sign is incorrectly referenced"
I look at some signatures made by others languages and libraries use a "reference-id" I assume that this is what ministry of finance is telling me, but how to do that with xadesjs?
@variux do you have an example document that they like?
Example of the signature label and its content:
In example.zip I attach a file with the wrong name. Instead of signatureSchema.xsd, it must be invoiceSchema.xsd.
Now, I attach a pdf with the schema of the invoice and the signature. Invoice&SignatureSchema.pdf
Thank you in advance for your help.
Hi a love this package!!! but I'm newbie on this stuff of digital signatures... Can guys give us an example of generate a XADES-EPES signature with xadesjs.
Thanks in advance for your colaboration