xml- cypto 数据
Xml 节点数字签名库. Xml加密即将到来. 写成纯洁的日记!
更多信息访问 我的博客 或者说 # 我的推特 # # my twitter #。 。 。 .
安装
安装方式 无数字 :
npm install xml-crypto
预设条件 打开 及其/bin在系统路径上。 我用了1.0.1c版本,但旧版本也应该有用.
支持的算法
分类和转换算法
- 强化 http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (中文(简体) )
- 带评论的分类 http://www.w3.org/TR/2001/REC-xml-c14n-20010315#With Comments
- 专有化 http://www.w3.org/2001/10/xml-exc-c14n#
- 带有评论的专有分类 http://www.w3.org/2001/10/xml-exc-c14n#With Comments
- 包含的签名转换 http://www.w3.org/2000/09/xmldsig#envelopted-signed-signed (中文(简体) )
汉字算法
- SHA1 摘要 http://www.w3.org/2000/09/xmldsig#sha1 (中文(简体) )
- SHA256摘要 http://www.w3.org/2001/04/xmlenc#sha256
- SHA512摘要 http://www.w3.org/2001/04/xmlenc#sha512 (中文(简体) )
签名算法
- RSA-SHA1号机车 http://www.w3.org/2000/09/xmldsig#rsa-sha1 (中文(简体) )
- RSA-SHA256号卫星 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 (中文(简体) )
- RSA-SHA512号 http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 (中文(简体) )
HMAC- SHA1 也可用, 但默认已禁用
以启用 HMAC-SHA1, 呼叫 enableHMAC() 以你为例 SignedXml。 。 。 .
这将启用HMAC并禁用数字签名算法. 由于密钥 使用基于HMAC的和公用的关键数字数据是很危险的 同时启用了签名算法 .
签名 Xml 文档
签名 xml 文档时, 您可以将以下选项传递到 SignedXml 自定义签名进程的构造器 :
privateKey- 说吧 [须 (单位:千美元)Buffer或 pem 编码String包含您的私钥publicCert- 说吧 [可选 (单位:千美元)Buffer或 pem 编码String包含您的公钥signatureAlgorithm- 说吧 [须 支持对象之一 签名算法。例如:sign.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"canonicalizationAlgorithm- 说吧 [须 支持对象之一 犬科化算法。例如:sign.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
使用此代码 :
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") });
sig.addReference({
xpath: "//*[local-name(.)='book']",
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});
sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
sig.computeSignature(xml);
fs.writeFileSync("signed.xml", sig.getSignedXml());
其结果是:
<library>
<book Id="_0">
<name>Harry Potter</name>
</book>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#_0">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>cdiS43aFDQMnb3X8yaIUej3+z9Q=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
</Signature>
</library>
说明:
如果你设定 publicCert 编辑 getKeyInfoContent 属性, a <KeyInfo></KeyInfo> 带有公共证书的元素将在签名中生成:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
...[signature info removed]...
</SignedInfo>
<SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIGYjCCBJagACCBN...[long base64 removed]...</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
对于 getKeyInfoContent,默认执行 SignedXml.getKeyInfoContent 已提供.
要自定义此视图 自定义算法 举个例子.
正在验证 Xml 文档
当验证 xml 文档时, 您可以将以下选项传递到 SignedXml 用于自定义验证进程的构造器 :
publicCert- 说吧 [可选 您的证书为字符串、 PEM 格式的多个证书的字符串或缓冲privateKey- 说吧 [可选 用于验证对称签名的私人密钥( HMAC)
用于检查签名的证书将首先通过调用 this.getCertFromKeyInfo(),哪个函数可以自定义。 如果这回 null,则 publicCert 已使用。 如果这是 null,则 privateKey 用于(对称签名应用程序).
示例
new SignedXml({
publicCert: client_public_pem,
getCertFromKeyInfo: () => null,
});
您可以在代码中使用任何您想要的多姆解析器( 或者没有, 取决于您的用法) 。 这个样本使用 缩写,所以你应该先安装它:
npm install @xmldom/xmldom
示例
var select = require("xml-crypto").xpath,
dom = require("@xmldom/xmldom").DOMParser,
SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = fs.readFileSync("signed.xml").toString();
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({ publicCert: fs.readFileSync("client_public.pem") });
sig.loadSignature(signature);
try {
var res = sig.checkSignature(xml);
} catch (ex) {
console.log(ex);
}
为了防止一些攻击 我们必须检查我们想要使用的内容 是已经签署的:
// Roll your own
const elem = xpath.select("/xpath_to_interesting_element", doc);
const uri = sig.getReferences()[0].uri; // might not be 0; it depends on the document
const id = uri[0] === "#" ? uri.substring(1) : uri;
if (
elem.getAttribute("ID") != id &&
elem.getAttribute("Id") != id &&
elem.getAttribute("id") != id
) {
throw new Error("The interesting element was not the one verified by the signature");
}
// Get the validated element directly from a reference
const elem = sig.references[0].getValidatedElement(); // might not be 0; it depends on the document
const matchingReference = xpath.select1("/xpath_to_interesting_element", elem);
if (!isDomNode.isNodeLike(matchingReference)) {
throw new Error("The interesting element was not the one verified by the signature");
}
// Use the built-in method
const elem = xpath.select1("/xpath_to_interesting_element", doc);
try {
const matchingReference = sig.validateElementAgainstReferences(elem, doc);
} catch {
throw new Error("The interesting element was not the one verified by the signature");
}
// Use the built-in method with a an xpath expression
try {
const matchingReference = sig.validateReferenceWithXPath("/xpath_to_interesting_element", doc);
} catch {
throw new Error("The interesting element was not the one verified by the signature");
}
说明:
Xml- crypto api 需要您单独提供 xml 签名(" signature"... </ signature"),在 loadSignature 中)和所签名的 xml (在 检查Signature 中). 签名的 xml 可能包含也可能不包含签名, 但您仍然需要单独提供签名 .
关心隐性转变
如果无法校验签名的XML,那么一个可能的原因是存在一些隐藏的隐性变换(#).
(#)使待核查的XML文档正常化. 即去除标签内的额外空间,排序属性,导入在祖先节点声明的命名空间等.
这些暗中转变的原因可能来自: 复杂 xml 签名规格, (中文(简体) ), 它使 XML 开发者混淆,然后导致签名 XML 文档的不正确执行.
如果您一直无法进行验证, 值得猜测这样一个隐藏的变换, 并将其指定为以下选项 :
var options = {
implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
publicCert: fs.readFileSync("client_public.pem"),
};
var sig = new SignedXml(options);
sig.loadSignature(signature);
var res = sig.checkSignature(xml);
你可能觉得很难猜到这种变换,但有些典型的变换你可以尝试.
- http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (中文(简体) )
- http://www.w3.org/TR/2001/REC-xml-c14n-20010315#With Comments
- http://www.w3.org/2001/10/xml-exc-c14n#
- http://www.w3.org/2001/10/xml-exc-c14n#With Comments
API 密码
ưμ㼯a
见 页:1 用于使用。 请注意,这实际上是在使用 另一个图书馆 作为执行的基础.
签名Xml
这个 SignedXml 构造器提供了签名和验证xml文档的抽象. 对象使用 new SignedXml(options?: SignedXmlOptions) 可能的选择是:
idMode- 默认null- 如果值为:wssecurity将会通过ws安全命名空间来创建/验证 id's.idAttribute- 字符串 - 默认Id或者说ID或者说id- 包含元素 id 的属性名称privateKey- 字符串或缓冲 - 默认null- 签字用的私人钥匙publicCert- 字符串或缓冲 - 默认null- 用于核查的公证书signatureAlgorithm- 字符串 - 要使用的签名算法canonicalizationAlgorithm- 字符串 - 默认undefined- 拟使用的划线化算法inclusiveNamespacesPrefixList- 字符串 - 默认null- 名称空间前缀列表,以包含在犬科化期间implicitTransforms- 字符串[] - 默认[]- 核查期间使用的隐性变换清单keyInfoAttributes- 对象 - 默认{}- 属性和价值的散列attrName: value添加到密钥信息节点getKeyInfoContent- 函数 - 默认noop- 返回 KeyInfo 节点内容的函数getCertFromKeyInfo- 函数 - 默认SignedXml.getCertFromKeyInfo- 返回证书的函数<KeyInfo />节点
API 密码
A 类 SignedXml 对象提供下列方法:
要签署 xml 文档 :
addReference(xpath, transforms, digestAlgorithm)- 增加一个xml元素:computeSignature(xml, [options])- 计算给定的 xml 的签名:xml- 包含 xml 文档的字符串options- 具有下列特性的物体:prefix- 作为生成的签名标记的前缀添加此值attrs- 属性和价值的散列attrName: value添加到签名根节点location- 定制签名位置,用reference键,该键应包含用于引用节点的 XPath 表达式, aaction键,其中应包含下列值之一:append, (中文(简体) ),prepend, (中文(简体) ),before, (中文(简体) ),afterexistingPrefixes- 一系列前缀和命名空间prefix: namespace这不应该在签名中,因为它们已经存在于 xml
getSignedXml()- 返回带有签名的正本 xml 文档, 必须在computeSignaturegetSignatureXml()- 仅返回签名部分, 必须在computeSignaturegetOriginalXmlWithIds()- 返回在相关元素上添加的 Id 属性的正本 xml(需要验证), 必须在computeSignature
要验证 xml 文档 :
loadSignature(signatureXml)- 将签名装入:signatureXml- 字符串或节点对象(如 缩写 节点),包含签名的 xml 表示
checkSignature(xml)- 验证给定的 xml 文档并返回true如果验证成功
自定义算法
以下样本显示如何使用自定义算法来签名消息.
首先导入一些模块 :
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
现在定义要执行的扩展点。 你可以选择一个或多个.
确定是否列入和内容 <KeyInfo /> 元素,函数
this.getKeyInfoContent() 名为. 缺省执行。 如果你想改变
此项执行,请提供您自己指定给财产的功能 this.getKeyInfoContent。如果您喜欢使用默认执行,请指定 SignedXml.getKeyInfoContent 改为 this.getKeyInfoContent 若为
没有属性和内容 <KeyInfo /> 元素,不会包含在
生成了 XML .
指定自定义属性 <KeyInfo />,将属性添加到 .keyInfoAttributes 属性.
自定义散列算法用于计算文摘. 如果您想要内置方法以外的散列, 请执行 .
function MyDigest() {
this.getHash = function (xml) {
return "the base64 hash representation of the given xml string";
};
this.getAlgorithmName = function () {
return "http://myDigestAlgorithm";
};
}
自定义签名算法 .
function MySignatureAlgorithm() {
/*sign the given SignedInfo using the key. return base64 signature value*/
this.getSignature = function (signedInfo, privateKey) {
return "signature of signedInfo as base64...";
};
this.getAlgorithmName = function () {
return "http://mySigningAlgorithm";
};
}
自定义转换算法 .
function MyTransformation() {
/*given a node (from the xmldom module) return its canonical representation (as string)*/
this.process = function (node) {
//you should apply your transformation before returning
return node.toString();
};
this.getAlgorithmName = function () {
return "http://myTransformation";
};
}
自定义的cononicalization实际上与自定义转换相同. 它适用于签署的信息而不是参考文献.
function MyCanonicalization() {
/*given a node (from the xmldom module) return its canonical representation (as string)*/
this.process = function (node) {
//you should apply your transformation before returning
return "< x/>";
};
this.getAlgorithmName = function () {
return "http://myCanonicalization";
};
}
现在你需要注册新的算法:
/*register all the custom algorithms*/
signedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation;
signedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization;
signedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest;
signedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm;
现在做签名。 注意我们如何配置使用上述算法的签名 :
function signXml(xml, xpath, key, dest) {
var options = {
publicCert: fs.readFileSync("my_public_cert.pem", "latin1"),
privateKey: fs.readFileSync(key),
/*configure the signature object to use the custom algorithms*/
signatureAlgorithm: "http://mySignatureAlgorithm",
canonicalizationAlgorithm: "http://MyCanonicalization",
};
var sig = new SignedXml(options);
sig.addReference({
xpath: "//*[local-name(.)='x']",
transforms: ["http://MyTransformation"],
digestAlgorithm: "http://myDigestAlgorithm",
});
sig.addReference({
xpath,
transforms: ["http://MyTransformation"],
digestAlgorithm: "http://myDigestAlgorithm",
});
sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
sig.computeSignature(xml);
fs.writeFileSync(dest, sig.getSignedXml());
}
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>";
("</library>");
signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml");
你总是可以把实际代码看成样本.
同步签名和核查
如果私密密钥不在本地存储,而您希望使用签名服务器或硬件安全模块(HSM)来签名文档,您可以创建一个使用同步回调的自定义签名算法.
function AsyncSignatureAlgorithm() {
this.getSignature = function (signedInfo, privateKey, callback) {
var signer = crypto.createSign("RSA-SHA1");
signer.update(signedInfo);
var res = signer.sign(privateKey, "base64");
//Do some asynchronous things here
callback(null, res);
};
this.getAlgorithmName = function () {
return "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
};
}
var sig = new SignedXml({ signatureAlgorithm: "http://asyncSignatureAlgorithm" });
sig.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm;
sig.signatureAlgorithm = "http://asyncSignatureAlgorithm";
sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
sig.computeSignature(xml, opts, function (err) {
var signedResponse = sig.getSignedXml();
});
职能 sig.checkSignature 如果需要同步核查,也可使用回调.
X.509 / 密钥格式
Xml-Crypto内部依赖于节点的加密模块. 这意味着pem被编码的证书被支持. 因此,要签署一个 xml 使用这样的 key. pem( 只显示密钥内容的开头) :
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0...
-----END PRIVATE KEY-----
而用于验证的密钥 public.pem:
-----BEGIN CERTIFICATE-----
MIIBxDCCAW6gAwIBAgIQxUSX...
-----END CERTIFICATE-----
将 pfx 证书转换为 pem
如果您有 .pfx 证书, 您可以使用 打开数字 :
openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem
那你可以用结果来签署 为验证目的,打开所生成的.pem并使用文本编辑器并复制自---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------.
实例
如何签名根节点( e)马上就来了(中文(简体) )
如何为签名添加前缀
使用 prefix 选项 computeSignature 为签名添加前缀.
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") });
sig.addReference({
xpath: "//*[local-name(.)='book']",
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});
sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
sig.computeSignature(xml, {
prefix: "ds",
});
如何指定签名的位置
使用 location 选项 computeSignature 来移动签名.
设定 action 改为:
- 附加( 默认) - 附加到 xml 文档的末尾
- 预选 - 预选到 xml 文档
- 前 - 预置到特定节点(使用
referenceNode(财产) - 在 - 附加到特定节点后(使用
referenceNode(财产)
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") });
sig.addReference({
xpath: "//*[local-name(.)='book']",
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});
sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
sig.computeSignature(xml, {
location: { reference: "//*[local-name(.)='book']", action: "after" }, //This will place the signature after the book element
});
更多实例(马上就来了(中文(简体) )
发展
测试使用:
npm test
