Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
XAdES is short for "XML Advanced Electronic Signatures", it is a superset of XMLDSIG. This library aims to provide an implementation of XAdES in Typescript/Javascript that is built on XMLDSIGjs.
Since it is based on XMLDSIGjs and that library uses Web Crypto for cryptographic operations it can be used both in browsers and in Node.js (when used with a polyfill like webcrypto, node-webcrypto-ossl or node-webcrypto-p11).
There are seven different profiles of XAdES, they are:
They differ slightly based on what is included in the signature:
Provides Digital Signature | Includes Cryptographic Timestamp | Includes Revocation References | Includes Revocation Data | Allows Secure Timestamp Countersignature | |
---|---|---|---|---|---|
XAdES-BES | Yes | No | No | No | No |
XAdES-EPES | Yes | No | No | No | No |
XAdES-T | Yes | Yes | No | No | No |
XAdES-C | Yes | Yes | Yes | No | No |
XAdES-X | Yes | Yes | Yes | No | No |
XAdES-X-L | Yes | Yes | Yes | Yes | No |
XAdES-A | Yes | Yes | Yes | Yes | Yes |
npm install xadesjs
The npm module has a dist
folder with the following files:
Name | Size | Description |
---|---|---|
index.js | 105 Kb | UMD module with external modules. Has comments |
xades.js | 803 Kb | UMD bundle module. Has comments |
xades.min.js | 296 Kb | minified UMD bundle module |
There is also a lib
folder with an ES2015 JS file which you can use with rollup
bundler.
Name | SHA1 | SHA2-256 | SHA2-384 | SHA2-512 |
---|---|---|---|---|
RSASSA-PKCS1-v1_5 | X | X | X | X |
RSA-PSS | X | X | X | X |
ECDSA | X | X | X | X |
HMAC | X | X | X | X |
XAdESjs works with any browser that suppports Web Crypto. Since node does not have Web Crypto you will need a polyfill on this platform, for this reason the npm package includes webcrypto; browsers do not need this dependency and in those cases though it will be installed it will be ignored.
If you need to use a Hardware Security Module we have also created a polyfill for Web Crypto that supports PKCS #11. Our polyfill for this is node-webcrypto-p11.
To use node-webcrypto-ossl you need to specify you want to use it, that looks like this:
var xadesjs = require("./built/xades.js");
var { Crypto } = require("@peculiar/webcrypto");
xadesjs.Application.setEngine("NodeJS", new Crypto());
The node-webcrypto-p11 polyfill will work the same way. The only difference is that you have to specify the details about your PKCS #11 device when you instansiate it:
var xadesjs = require("./built/xades.js");
var WebCrypto = require("node-webcrypto-p11").WebCrypto;
xadesjs.Application.setEngine("PKCS11", new WebCrypto({
library: "/path/to/pkcs11.so",
name: "Name of PKCS11 lib",
slot: 0,
sessionFlags: 2 | 4, // RW_SESSION | SERIAL_SESSION
pin: "token pin"
}));
Using XMLDSIG is a bit like running with scissors, that said it is needed for interoperability with a number of systems, for this reason, we have done this implementation.
SignedXml.Sign(algorithm: Algorithm, key: CryptoKey, data: Document, options?: OptionsXAdES): PromiseLike<Signature>;
Parameters
Name | Description |
---|---|
algorithm | Signing Algorithm |
key | Signing Key |
data | XML document which must be signed |
options | Additional options |
interface OptionsXAdES {
/**
* Public key for KeyInfo block
*/
keyValue?: CryptoKey;
/**
* List of X509 Certificates
*/
x509?: string[];
/**
* List of Reference
* Default is Reference with hash alg SHA-256 and exc-c14n transform
*/
references?: OptionsSignReference[];
// Signed signature properties
signingCertificate?: string;
signingTime?: OptionsSigningTime;
policy?: OptionsPolicyId;
productionPlace?: OptionsProductionPlace;
signerRole?: OptionsSignerRole;
}
interface OptionsSignReference {
/**
* Id of Reference
*/
id?: string;
uri?: string;
/**
* Hash algorithm
*/
hash: AlgorithmIdentifier;
/**
* List of transforms
*/
transforms?: OptionsSignTransform[];
}
type OptionsSignTransform = "enveloped" | "c14n" | "exc-c14n" | "c14n-com" | "exc-c14n-com" | "base64";
interface OptionsSigningTime {
value?: Date;
format?: string;
}
interface OptionsSignerRole {
claimed?: string[];
certified?: string[];
}
interface OptionsProductionPlace {
city?: string;
state?: string;
code?: string;
country?: string;
}
interface OptionsPolicyId {
}
Verify(key?: CryptoKey): PromiseLike<boolean>;
Parameters
Name | Description |
---|---|
key | Verifying Key. Optional. If key not set it looks for keys in KeyInfo element of Signature. |
var xadesjs = require("xadesjs");
var { Crypto } = require("@peculiar/webcrypto");
xadesjs.Application.setEngine("NodeJS", new Crypto());
// Generate RSA key pair
var privateKey, publicKey;
xadesjs.Application.crypto.subtle.generateKey(
{
name: "RSASSA-PKCS1-v1_5",
modulusLength: 1024, //can be 1024, 2048, or 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: { name: "SHA-1" }, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
},
false, //whether the key is extractable (i.e. can be used in exportKey)
["sign", "verify"] //can be any combination of "sign" and "verify"
)
.then(function (keyPair) {
// Push ganerated keys to global variable
privateKey = keyPair.privateKey;
publicKey = keyPair.publicKey;
// Call sign function
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>';
return SignXml(xmlString, keyPair, { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-1" } });
})
.then(function (signedDocument) {
console.log("Signed document:\n\n", signedDocument);
})
.catch(function (e) {
console.error(e);
});
function SignXml(xmlString, keys, algorithm) {
return Promise.resolve()
.then(() => {
var xmlDoc = xadesjs.Parse(xmlString);
var signedXml = new xadesjs.SignedXml();
return signedXml.Sign( // Signing document
algorithm, // algorithm
keys.privateKey, // key
xmlDoc, // document
{ // options
keyValue: keys.publicKey,
references: [
{ hash: "SHA-256", transforms: ["enveloped"] }
],
productionPlace: {
country: "Country",
state: "State",
city: "City",
code: "Code",
},
signingCertificate: "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"
})
})
.then(signature => signature.toString());
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>XADESJS Signature Sample</title>
</head>
<body>
<pre id="signature"><code></code></pre>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.7.0/polyfill.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/asmCrypto/2.3.2/asmcrypto.all.es5.min.js"></script>
<script src="https://cdn.rawgit.com/indutny/elliptic/master/dist/elliptic.min.js"></script>
<script src="https://unpkg.com/webcrypto-liner@1.1.2/build/webcrypto-liner.shim.min.js"></script>
<script src="https://unpkg.com/xadesjs@2.0.16/build/xades.js"></script>
<script type="text/javascript">
// Generate RSA key pair
var privateKey, publicKey;
window.crypto.subtle.generateKey(
{
name: "ECDSA",
namedCurve: "P-256"
},
false, //whether the key is extractable (i.e. can be used in exportKey)
["sign", "verify"] //can be any combination of "sign" and "verify"
)
.then(function (keyPair) {
// Push ganerated keys to global variable
privateKey = keyPair.privateKey;
publicKey = keyPair.publicKey;
// Call sign function
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>';
return SignXml(xmlString, keyPair, { name: "ECDSA", hash: { name: "SHA-1" } });
})
.then(function (signedDocument) {
document.getElementById("signature").textContent = signedDocument;
console.log("Signed document:\n\n", signedDocument);
})
.catch(function (e) {
console.error(e);
});
function SignXml(xmlString, keys, algorithm) {
var signedXml;
return Promise.resolve()
.then(() => {
var xmlDoc = XAdES.Parse(xmlString);
signedXml = new XAdES.SignedXml();
return signedXml.Sign( // Signing document
algorithm, // algorithm
keys.privateKey, // key
xmlDoc, // document
{ // options
keyValue: keys.publicKey,
references: [
{ hash: "SHA-256", transforms: ["enveloped"] }
],
productionPlace: {
country: "Country",
state: "State",
city: "City",
code: "Code",
},
signerRole: {
claimed: ["Some role"]
}
})
})
.then(() => signedXml.toString());
}
</script>
</body>
</html>
var XAdES = require("xadesjs");
var { Crypto } = require("@peculiar/webcrypto");
XAdES.Application.setEngine("NodeJS", new Crypto());
var fs = require("fs");
var xmlString = fs.readFileSync("some.xml","utf8");
var signedDocument = XAdES.Parse(xmlString, "application/xml");
var xmlSignature = signedDocument.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
var signedXml = new xadesjs.SignedXml(signedDocument);
signedXml.LoadXml(xmlSignature[0]);
signedXml.Verify()
.then(res => {
console.log((res ? "Valid" : "Invalid") + " signature");
})
.catch(function (e) {
console.error(e);
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>XADESJS Signature Sample</title>
</head>
<body>
<pre id="signature"><code></code></pre>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.7.0/polyfill.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/asmCrypto/2.3.2/asmcrypto.all.es5.min.js"></script>
<script src="https://cdn.rawgit.com/indutny/elliptic/master/dist/elliptic.min.js"></script>
<script src="https://unpkg.com/webcrypto-liner@1.1.2/build/webcrypto-liner.shim.min.js"></script>
<script src="https://unpkg.com/xadesjs@2.0.16/build/xades.js"></script>
<script type="text/javascript">
"use strict";
fetch("https://cdn.rawgit.com/PeculiarVentures/xadesjs/master/test/static/valid_signature.xml")
.then(response => response.text())
.then(body => {
var xmlString = body;
var signedDocument = XAdES.Parse(xmlString);
var xmlSignature = signedDocument.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
var signedXml = new xadesjs.SignedXml(signedDocument);
signedXml.LoadXml(xmlSignature[0]);
signedXml.Verify()
.then(function (signedDocument) {
alert((res ? "Valid" : "Invalid") + " signature");
})
.catch(function (e) {
alert(e.message);
});
})
</script>
</body>
</html>
const fs = require("fs");
var { Crypto } = require("@peculiar/webcrypto");
const xadesjs = require("xadesjs");
const { XMLSerializer } = require("xmldom");
const crypto = new Crypto();
xadesjs.Application.setEngine("NodeJS", );
function preparePem(pem) {
return pem
// remove BEGIN/END
.replace(/-----(BEGIN|END)[\w\d\s]+-----/g, "")
// remove \r, \n
.replace(/[\r\n]/g, "");
}
function pem2der(pem) {
pem = preparePem(pem);
// convert base64 to ArrayBuffer
return new Uint8Array(Buffer.from(pem, "base64")).buffer;
}
async function main() {
const hash = "SHA-256"
const alg = {
name: "RSASSA-PKCS1-v1_5",
hash,
}
// Read cert
const certPem = fs.readFileSync("cert.pem", { encoding: "utf8" });
const certDer = pem2der(certPem);
// Read key
const keyPem = fs.readFileSync("key.pem", { encoding: "utf8" });
const keyDer = pem2der(keyPem);
const key = await crypto.subtle.importKey("pkcs8", keyDer, alg, false, ["sign"]);
// XAdES-EPES
var xmlString = `<Test><Document attr="Hello"/></Test>`;
var xml = xadesjs.Parse(xmlString);
var xadesXml = new xadesjs.SignedXml();
const x509 = preparePem(certPem);
const signature = await xadesXml.Sign( // Signing document
alg, // algorithm
key, // key
xml, // document
{ // options
references: [
{ hash, transforms: ["c14n", "enveloped"] }
],
policy: {
hash,
identifier: {
qualifier: "OIDAsURI",
value: "quilifier.uri",
},
qualifiers: [
{
noticeRef: {
organization: "PeculiarVentures",
noticeNumbers: [1, 2, 3, 4, 5]
}
}
]
},
productionPlace: {
country: "Russia",
state: "Marij El",
city: "Yoshkar-Ola",
code: "424000",
},
signingCertificate: x509
});
// append signature
xml.documentElement.appendChild(signature.GetXml());
// serialize XML
const oSerializer = new XMLSerializer();
const sXML = oSerializer.serializeToString(xml);
console.log(sXML.toString())
}
main()
.catch((err) => {
console.error(err);
});
npm test
This project takes inspiration (style, approach, design and code) from both the Mono System.Security.Cryptography.Xml implementation as well as xml-crypto.
FAQs
A pure Typescript/Javascript implementation of XAdES based on XMLDSIGjs.
We found that xadesjs demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.