- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用封装签名、sha1 摘要和 rss-sha1 签名按照 XMLDSIG 规范对 xml 文档进行签名,并且服务器不断返回“297 - 拒绝:签名与计算的[结果]不匹配”(巴西葡萄牙语原版“297 - Rejeicao:Assinatura difere do calculado”)
我的客户端应用程序需要是 Mac OS X 原生的(因此是 Objective-C 和 Swift)。我遵守 Apple 的 CryptoCompatibility 准则并使用 Security.framework 的 SecSignTransform 和 CommonCrypto 的 CC_SHA1。
这是我正在尝试 XMLDSIG 的 XML(没有 PrettyPrint 和省略术语以节省空间):
<NFe xmlns="http://www.portalfiscal.inf.br/nfe"><infNFe Id="NFe351503...1455341071" versao="2.00"><ide><cUF>35</cUF><cNF>45534107</cNF><natOp>VENDA</natOp><indPag>1</indPag><mod>55</mod>
...
</infNFe><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI="#NFe351503...1455341071"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>H4l0eMA6H4ndKzY3ftwlsKpeX58=</DigestValue></Reference></SignedInfo><SignatureValue>QYMVPWvZOeF4XgorObl33Tm9DiZEW4N7zuuAbt9Jjop79V41SNAIO5qIXe06cLiJACghi1X+p3pROE3P/E/lhPhwGmA3G26Jm5hZqsGhURS1osHDNKDWARBpi+musgi5naHm4tKqlKKIKqARljyXyYRRVaoxOSrC3vmxPx2ClwwTrlgnqtDTODQU0yNN4OUXTxWAMYPm8rc2rO6OUohTK+eXE3mN5vgCB6GLMWj0Cp2k6N21264WNv/P+L45kHUllFnV+ByMshXFYzySvthujlq/4ClSG+1xOFYMATn1F6qvklMDXy7bS+Dqcp635ZFxfD97gTDriFUYH0+nEe95zw==</SignatureValue><KeyInfo><X509Data><X509Certificate>...</X509Certificate></X509Data></KeyInfo></Signature></NFe>
不幸的是,由于 .Net 和 Java 都为 XMLDSIG 提供了非常高级别的支持,因此关于要获取 XML 的哪些部分、要保留什么以及要删除什么的详细信息在 Internet 上很少见。除了 W3.org 自己的非常枯燥的规范外,我找到的唯一深入的解释是:http://www.di-mgt.com.au/xmldsig2.html
我不确定不匹配是在 sha1 摘要还是 rsa-sha1 签名中,返回码不清楚。另外,我不知道我是否使用了错误的输入,或者我使用的 Mac OS X 库是否与服务器(基于 .Net)不兼容。
这是摘要的代码。请注意它使用了相同文档的 URI 引用:
// Imports XML data from XML file
var xmlStr: String? = File.open(documentPath)
// gets Id (formated "NFe" + 44x[0-9]) to create SignedInfo reference URI
let myId = getNFeId(xmlStr!)
// creates a XML Document using String xmlStr and canonicalize "c14n"
var xmlDocument = XMLSupportClass.createXMLDocument(xmlStr!)
let canonicalXmlStr = xmlDocument.canonicalXMLStringPreservingComments(false)
var stringToDigest = ""
// retrieves element referenced by URI (#myId) to create digest
if (xmlDocument.rootElement() != nil) {
let xmlRoot: NSXMLElement = xmlDocument.rootElement()!
// let myURI = "#" + myId
// let nodesToTest: [NSXMLElement] = xmlRoot.elementsForLocalName("NFe", URI: myURI) as [NSXMLElement]
// let nodesToTest2: [NSXMLElement] = xmlRoot.elementsForName("infNFe") as [NSXMLElement]
let myXPath: String = "//*[@Id=\'" + myId + "\']"
let nodesToDigest = xmlRoot.nodesForXPath(myXPath, error: &xpathError) as [NSXMLElement]
if nodesToDigest.count > 0 {
stringToDigest = nodesToDigest[0].canonicalXMLStringPreservingComments(false)
} else { println(xpathError) }
} else {
println("I'm root-less!!")
}
// creates the digest using CryptoCompatibility
digestData = stringToDigest.sha1()
let digestDataAsString: String = digestData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithLineFeed)
代码中使用的额外方法:
func getNFeId(xml: String) -> String {
// mas como extrair o atributo Id do elemento <infNFe>
var myError: NSError?
let root = XMLSupportClass.createXMLDocument(xml).rootElement()! as NSXMLElement
let infNodes = root.elementsForName("infNFe") as [NSXMLElement]
if infNodes.count > 0 {
let idNode = infNodes[0].attributeForName("Id")! as NSXMLNode
let myId = idNode.objectValue as String
println(myId)
return myId
} else {
println("error extracting NFeId")
return "error extracting NFeId"
}
}
// SHA-1 Digest from CryptoCompatibility returning a Hex String
extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in digest {
output.appendFormat("%02x", byte)
}
return output
}
}
计算摘要后,将其插入到预先格式化的Signature XML String中,创建一个XML Document,然后提取SignedInfo节点并用于生成SignatureValue:
// pre-formatted XML String for Signature node, leaving SignatureValue empty for filling in later
let xmlAssinatura = "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#\(myId)\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><Transform Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>\(digestDataAsString)</DigestValue></Reference></SignedInfo><SignatureValue></SignatureValue><KeyInfo><X509Data><X509Certificate>\(certDataAsString)</X509Certificate></X509Data></KeyInfo></Signature>"
// tranforms xmlAssinatura String in NSXMLDocument
var xmlAssinaturaDocument = XMLSupportClass.createXMLDocument(xmlAssinatura)
let signatureNode = xmlAssinaturaDocument.rootElement()!
// and retrieves SignedInfo node, converts to NSData for signing
let xmlSignedInfoElement = (signatureNode.elementsForName("SignedInfo") as [NSXMLElement])[0]
**// ====> the line below was the problem!!!**
/*let signedInfoData = XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false)).XMLData */
**// ====> and this is the fix:**
let signedInfoData = (XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false), withTidyXML:true).rootElement()!.XMLString).dataUsingEncoding(NSUTF8StringEncoding)!
// creates SecTransform object
signer = SecSignTransformCreate(priKey, &error).takeRetainedValue()
if error != nil { print("signer transform creation error: ") ; println(error) }
// signer to use SHA1 digest method and use signedInfoData as input
SecTransformSetAttribute(signer, kSecDigestTypeAttribute, kSecDigestSHA1, &error)
if error != nil { print("verifier digest attribute setting error: ") ; println(error) }
SecTransformSetAttribute(signer, kSecTransformInputAttributeName, signedInfoData, &error)
if error != nil { print("signer attribute setting error: ") ; println(error) }
// executes the transform
signedData = (SecTransformExecute(signer, &error) as NSData)
let signedDataAsString = signedData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithLineFeed)
if error != nil { print("signer execute error: ") ; println(error) }
// inserts generated signedDataAsString in <SignatureValue> node
let signatureValueElements = signatureNode.elementsForName("SignatureValue") as [NSXMLElement]
if signatureValueElements.count > 0 { signatureValueElements[0].setStringValue(signedDataAsString, resolvingEntities: false) } else { println(signatureValueElements) }
signatureNode.detach()
xmlAssinaturaDocument = nil
// then replaces <Signature> placeholder node in xmlDocument
if (xmlDocument.rootElement() != nil) {
let xmlRoot: NSXMLElement = xmlDocument.rootElement()!
let signatureNodePlaceholder: NSXMLElement = (xmlRoot.elementsForName("Signature") as [NSXMLElement])[0]
let signatureNodeIndex = signatureNodePlaceholder.index
xmlRoot.replaceChildAtIndex(signatureNodeIndex, withNode: signatureNode)
// and creates xmlDocument canonicalized String
xmlStr = xmlRoot.canonicalXMLStringPreservingComments(false)
}
据我所知,一切都是正确的,并且符合 W3.org 的 XMLDSIG 规范。尽管如此,服务器始终拒绝生成的 XML。
我已经无计可施了。任何帮助和智慧将不胜感激!!
最佳答案
找到了!!
这是我做错的地方:
我的代码将 NSXMLDocument 直接转换为 NSData,但这意味着 XML 类型 <?xml version="1.0" encoding="UTF-8"?>
一开始就被包括在内。
经过一番摆弄以找到正确的表达式后,我替换了这段代码:
let signedInfoData = XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false)).XMLData
使用此代码:
let signedInfoData = (XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false), withTidyXML:true).rootElement()!.XMLString).dataUsingEncoding(NSUTF8StringEncoding)!
成功了!!
可能是因为缺少命名空间<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
包括在内,但不包括 XML 类型声明...
如前所述,XMLDSIG 非常注重细节。可惜在 Objective-C 或 Swift 中没有高级支持,但我猜 Apple 认为它太过时了。
有经验的程序员现在无法将我的代码变成设计良好的库并提供这种高级支持。只希望我的经历对追随我道路的任何人有用...
关于xml - 究竟要在 XMLDSIG 上消化和签署什么以及如何消化? (或者,OSX 本地客户端与服务器上计算的 XMLDSIG 不匹配),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29042452/
我试图用 Python 编写理论上的蛋白质序列胰蛋白 enzyme 切割代码。胰蛋白 enzyme 的切割规则是:在 R 或 K 之后,但不在 P 之前。(即胰蛋白 enzyme 在每个 K 或 R
我正在消化其他一些 zip 文件的内容以生成 MD5。文件内容被摘要并生成 MD5,而不是例如基于时间戳生成 MD5。因此,我断言两个文件具有相同的内容,即使它们是在不同时间生成的。因此,我编写了以下
我是一名优秀的程序员,十分优秀!