2

エンベロープ署名、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 のどの部分を取得し、何を保持し、何を削除するかに関する詳細な情報は、インターネット上にほとんどありません。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
    }
}

ダイジェストを計算した後、XML ドキュメントを作成するために、事前にフォーマットされた署名 XML 文字列に挿入されます。次に、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 を常に拒否します。

私は機知に富んでいます。どんな助けと知恵も大歓迎です!!

4

1 に答える 1