Попробуем подпись XML
XMLDSig: формат подписи XML
Заходим
github.com/yaronn/xml-crypto
Читаем
В новом директории xml-crypto делаем
npm install xml-crypto
npm install node-forge
Копируем туда ключи
AUTH_....p12
RSA256_....p12
Подправляем с условием, чтоб ключи и сертификаты читаем через node-forge
Копируем example.js в example.bak.js
Для того, чтобы в последующем оценить размер xml-crypto bundle'а для варианта с браузером.
xml-crypto\node_modules\xml-crypto\example\example.js
var forge = require('node-forge');
var select = require('xml-crypto').xpath
, dom = require('xmldom').DOMParser
, SignedXml = require('xml-crypto').SignedXml
, FileKeyInfo = require('xml-crypto').FileKeyInfo
, fs = require('fs')
function SignKeyInfo(prefix) {
this.getKeyInfo = function(key, prefix) {
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return "<" + prefix + "X509Data><" + prefix + "X509Certificate>" +
removeHeaderFromPem(forge.pki.certificateToPem(cert)) +
"</" + prefix + "X509Certificate></" + prefix + "X509Data>"
}
this.getKey = function(keyInfo) {
//you can use the keyInfo parameter to extract the key in any way you want
return forge.pki.privateKeyToPem(privateKey)
}
}
function ValidateKeyInfo(prefix) {
this.getKeyInfo = function(key, prefix) {
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return "<" + prefix + "X509Data><" + prefix + "X509Certificate>" +
removeHeaderFromPem(forge.pki.certificateToPem(cert)) +
"</" + prefix + "X509Certificate></" + prefix + "X509Data>"
}
this.getKey = function(keyInfo) {
//you can use the keyInfo parameter to extract the key in any way you want
return forge.pki.certificateToPem(cert)
}
}
function removeHeaderFromPem(pem) {
var lines = pem.split('\n');
var encoded = '';
for(var i = 0;i < lines.length;i++){
if (lines[i].trim().length > 0 &&
lines[i].indexOf('-BEGIN CERTIFICATE-') < 0 &&
lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 &&
lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END CERTIFICATE-') < 0 &&
lines[i].indexOf('-END PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-END RSA PUBLIC KEY-') < 0) {
encoded += lines[i].trim();
}
}
return encoded;
}
function signXml(xml, xpath, key, dest)
{
var sig = new SignedXml()
//sig.signingKey = fs.readFileSync(key)
sig.signingKey = key
sig.addReference(xpath)
sig.keyInfoProvider = new SignKeyInfo('ds')
sig.computeSignature(xml,{
prefix: 'ds'
})
fs.writeFileSync(dest, sig.getSignedXml())
}
function validateXml(xml, key)
{
var doc = new dom().parseFromString(xml)
var signature = select(doc, "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]
var sig = new SignedXml()
// sig.keyInfoProvider = new FileKeyInfo(key)
sig.keyInfoProvider = new ValidateKeyInfo('ds')
sig.loadSignature(signature.toString())
var res = sig.checkSignature(xml)
if (!res) console.log(sig.validationErrors)
return res;
}
//----------------------------------------------------------------
//Начало
var keyFile = fs.readFileSync("./RSA256_....p12", 'binary');
var p12Asn1 = forge.asn1.fromDer(keyFile);
// pkcs12 с паролем
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, '123456');
var bags = p12.getBags({bagType: forge.pki.oids.certBag});
// Сертификат
var certBag = bags[forge.pki.oids.certBag][0];
var cert = certBag.cert;
//console.log(forge.pki.certificateToPem(cert));
//-----BEGIN CERTIFICATE-----
// Приватный ключ
var keyBags = p12.getBags({bagType: forge.pki.oids.pkcs8ShroudedKeyBag});
var keyBag = keyBags[forge.pki.oids.pkcs8ShroudedKeyBag][0];
var privateKey = keyBag.key;
//console.log(forge.pki.privateKeyToPem(privateKey));
//-----BEGIN RSA PRIVATE KEY-----
// Публичный ключ
var publicKey = forge.pki.setRsaPublicKey(privateKey.n, privateKey.e);
//console.log(forge.pki.publicKeyToPem(publicKey));
//-----BEGIN PUBLIC KEY-----
//----------------------------------------------------------------------------------
// Проверка XMLDSIG
var xml = "<library>" +
"<book>" +
"<name>Harry Potter</name>" +
"</book>" +
"</library>"
//sign an xml document
//signXml(xml,
// "//*[local-name(.)='book']",
// "client.pem",
// "result.xml")
// Подписываем приватным ключом
signXml(xml,
"//*[local-name(.)='book']",
forge.pki.privateKeyToPem(privateKey),
"result.xml")
console.log("xml signed succesfully")
var signedXml = fs.readFileSync("result.xml").toString()
console.log("validating signature...")
//validate an xml document
//if (validateXml(signedXml, "client_public.pem"))
//Проверяем сертификатом
if (validateXml(signedXml, forge.pki.certificateToPem(cert)))
console.log("signature is valid")
else
console.log("signature not valid")
Запускаем
node example.js
Должно вывестись
"signature is valid"
Подписанный XML получается такой
ВНИМАНИЕ: СПОЙЛЕР!
<library>
<book Id="_0">
<name>Harry Potter</name>
</book>
<ds:Signature xmlns:ds="www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#_0">
<ds:Transforms>
<ds:Transform Algorithm="www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>cdiS43aFDQMnb3X8yaIUej3+z9Q=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>dzNiT4sGyp2+0W7Yub9qAFu//xMWOxMgveAFiVkRva7nxZH4uuF37FJQ1ZWdn6VfbRCIMiI5pD4G7VVF2bGoRZGonKPgv4uYGm7OCXyfcaMfdjdOdtSvDaMVDHKU75DJZIcpD3NnVbvJq1ubUm7gBto3HgCfjvy7pqhN6Io3sxuKlJxts+31BCJruVPmsyE6YhzpEc67uidm1Pp+r7Sa/RoHSeJmiDoNXW9jbGGg+dl3FZG0jpF9JfhX0JyfYDdk9BqQZOpxO6QOIqmHbv6AmXjMWFN82gGzJHb68UfE2DDUaV96N7ApsPZQBjyUT9wd1UmXlEEv4gxbihu/q0ERMA==</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>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</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</library>