Bonjour à tous.
Quelques années auparavant, j'avais fait un code en vb.net afin de signer des xml à l'aide d'un certificat et à placer la signature grâce à la valeur du xpath.
J'ai un contact qui aimerait se servir de mon code mais il aimerait l'avoir en java, langage que je ne maitrise pas pour ma part (je programme en wlangage, parfois en vb.net mais jamais en java), et j'aimerais avoir votre aide pour faire cette migration de code.
Contexte : j'aimerais signé le fichier 20381235051-01-FF11-01.xml, en prenant la valeur se trouvant entre les 2 premiers tirets (ici, 01), je connais à l'avance la valeur du xpath, ainsi que de l'espace de nommage pour pouvoir générer mon document, le document sera signé à l'aide d'un certificat X509.
Voici mon code vb.net :
Mon contact a généré le code suivant en java :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 Try Dim local_xmlArchivo As String = "C:\XML\20381235051-01-FF11-01.xml" Dim local_nombreXML As String = System.IO.Path.GetFileName(local_xmlArchivo) Dim local_typoDocumento As String local_typoDocumento = local_nombreXML.Substring(12, 2) Dim MiCertificado As X509Certificate2 = New X509Certificate2("C:\Certificates\MyCert.p12", "myPWD") Dim xmlDoc As XmlDocument = New XmlDocument() xmlDoc.PreserveWhitespace = True xmlDoc.Load(local_xmlArchivo) Dim signedXml As SignedXml = New SignedXml(xmlDoc) signedXml.SigningKey = MiCertificado.PrivateKey Dim KeyInfo As KeyInfo = New KeyInfo() Dim Reference As Reference = New Reference() Reference.Uri = "" Reference.AddTransform(New XmlDsigEnvelopedSignatureTransform()) signedXml.AddReference(Reference) Dim X509Chain As X509Chain = New X509Chain() X509Chain.Build(MiCertificado) Dim local_element As X509ChainElement = X509Chain.ChainElements(0) Dim x509Data As KeyInfoX509Data = New KeyInfoX509Data(local_element.Certificate) Dim subjectName As String = local_element.Certificate.Subject x509Data.AddSubjectName(subjectName) KeyInfo.AddClause(x509Data) signedXml.KeyInfo = KeyInfo signedXml.ComputeSignature() Dim signature As XmlElement = signedXml.GetXml() signature.Prefix = "ds" signedXml.ComputeSignature() For Each node As XmlNode In signature.SelectNodes("descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']") If node.LocalName = "Signature" Then Dim newAttribute As XmlAttribute = xmlDoc.CreateAttribute("Id") newAttribute.Value = "SignatureSP" node.Attributes.Append(newAttribute) Exit For End If Next node Dim local_xpath As String Dim nsMgr As XmlNamespaceManager nsMgr = New XmlNamespaceManager(xmlDoc.NameTable) nsMgr.AddNamespace("sac", "urn:sunat:names:specification:ubl:peru:schema:xsd:SunatAggregateComponents-1") nsMgr.AddNamespace("ccts", "urn:un:unece:uncefact:documentation:2") nsMgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance") Select Case local_typoDocumento Case "01", "03" 'factura / boleta nsMgr.AddNamespace("tns", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2") local_xpath = "/tns:Invoice/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent" Case "07" 'nota de credito nsMgr.AddNamespace("tns", "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2") local_xpath = "/tns:CreditNote/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent" Case "08" 'nota de debito nsMgr.AddNamespace("tns", "urn:oasis:names:specification:ubl:schema:xsd:DebitNote-2") local_xpath = "/tns:DebitNote/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent" Case "09" 'Remision del remitente nsMgr.AddNamespace("tns", "urn:oasis:names:specification:ubl:schema:xsd:DespatchAdvice-2") local_xpath = "/tns:DespatchAdvice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent" Case "20" 'Retencion nsMgr.AddNamespace("tns", "urn:sunat:names:specification:ubl:peru:schema:xsd:Retention-1") local_xpath = "/tns:Retention/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent" Case "40" 'Percepcion nsMgr.AddNamespace("tns", "urn:sunat:names:specification:ubl:peru:schema:xsd:Perception-1") local_xpath = "/tns:Perception/ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent" Case "RA" 'Communicacion de baja nsMgr.AddNamespace("tns", "urn:sunat:names:specification:ubl:peru:schema:xsd:VoidedDocuments-1") local_xpath = "/tns:VoidedDocuments/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent" Case "RC" 'Resumen de diario nsMgr.AddNamespace("tns", "urn:sunat:names:specification:ubl:peru:schema:xsd:SummaryDocuments-1") local_xpath = "/tns:SummaryDocuments/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent" End Select nsMgr.AddNamespace("cac", "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2") nsMgr.AddNamespace("udt", "urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2") nsMgr.AddNamespace("ext", "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2") nsMgr.AddNamespace("qdt", "urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2") nsMgr.AddNamespace("cbc", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2") nsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#") xmlDoc.SelectSingleNode(local_xpath, nsMgr).AppendChild(xmlDoc.ImportNode(signature, True)) xmlDoc.Save(local_xmlArchivo) Dim nodeList As XmlNodeList = xmlDoc.GetElementsByTagName("ds:Signature") 'el nodo <ds:Signature> debe existir unicamente 1 vez If nodeList.Count <> 1 Then Throw New Exception("Se produjo un error en la firma del documento") End If signedXml.LoadXml(CType(nodeList(0), XmlElement)) 'verificacion de la firma generada If signedXml.CheckSignature() = False Then Throw New Exception("Se produjo un error en la firma del documento") End If Catch ex As Exception MsgBox(ex.Message) End Try
Problème rencontré : le fichier XML est signé mais cette signature n'est pas valide. J'ai remarqué que la signature générée ressemblait à cela :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 import java.io.File; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.logging.Level; import java.util.logging.Logger; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableEntryException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.Reference; import javax.xml.crypto.dsig.SignatureMethod; import javax.xml.crypto.dsig.SignedInfo; import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.keyinfo.KeyInfo; import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; import javax.xml.crypto.dsig.keyinfo.X509Data; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import javax.xml.crypto.dsig.spec.TransformParameterSpec; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class TraitementSignature { public static void main(String[] args) { String keystoreType = "JKS"; String keystoreFile = sENTORNO + "AlfaStore.jks"; String keystorePass = "alfa5612"; String certificateAlias = "ALFA001"; String privateKeyAlias = "ALFA002"; XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance("DOM"); Reference reference = signatureFactory.newReference("", signatureFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(signatureFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null); SignedInfo si = signatureFactory.newSignedInfo(signatureFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), signatureFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(reference)); KeyStore ks = KeyStore.getInstance(keystoreType); ks.load(new FileInputStream(keystoreFile), keystorePass.toCharArray()); KeyStore.TrustedCertificateEntry keyEntry = (KeyStore.TrustedCertificateEntry) ks.getEntry(certificateAlias, null); KeyStore.PrivateKeyEntry keyEntry2 = (KeyStore.PrivateKeyEntry) ks.getEntry(privateKeyAlias, new KeyStore.PasswordProtection(keystorePass.toCharArray())); X509Certificate cert = (X509Certificate) keyEntry.getTrustedCertificate(); KeyInfoFactory kif = signatureFactory.getKeyInfoFactory(); List x509Content = new ArrayList(); x509Content.add(cert.getIssuerX500Principal().getName()); x509Content.add(cert); // data requerida por la sunat X509Data xData = kif.newX509Data(x509Content); KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xData)); DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); documentFactory.setNamespaceAware(true); Document doc = documentFactory.newDocumentBuilder().parse(sXML); /** * En esta parte se busca el nodo del xml donde deberia ir la firma segun el documento * de la sunat. */ try { Element el = (Element) doc.getElementsByTagName("ext:UBLExtensions").item(0); NodeList ndl = el.getElementsByTagName("ext:UBLExtension"); Element signElement = (Element) ndl.item(1); Element contentSingElement = (Element) signElement.getElementsByTagName("ext:ExtensionContent").item(0); DOMSignContext domSingContext = new DOMSignContext(keyEntry2.getPrivateKey(),contentSingElement); domSingContext.setDefaultNamespacePrefix("ds"); XMLSignature signature = signatureFactory.newXMLSignature(si, ki); //extensions.appendChild(extensionContent); signature.sign(domSingContext); //Añadiendo el codigo de la firma al elemento requerido. Element signTag = (Element) contentSingElement.getElementsByTagName("ds:Signature").item(0); signTag.setAttribute("Id", "SignatureSP"); //Añadiendo el id que la sunat require para el tag de la firma //extensions.add(uBLExtension); } catch (Exception e){ System.out.println(e.toString()); } OutputStream os = new FileOutputStream(sXML2); TransformerFactory tf = TransformerFactory.newInstance(); Transformer trans = tf.newTransformer(); trans.transform(new DOMSource(doc), new StreamResult(os)); } }
Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 <ds:Signature Id="SignatureSP"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <ds:Reference URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>7rOmi9ZdzwobzJMvidIHNvuHk98=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>XXXXXXXXXXXXXXXXXXXX</ds:SignatureValue> <ds:KeyInfo> <ds:X509Data> <ds:X509SubjectName>CN=GlobalSign PersonalSign 2 CA - G3,O=GlobalSign nv-sa,C=BE</ds:X509SubjectName><ds:X509Certificate>XXXXXXXXXXXXXXXXXXX</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </ds:Signature>
Dans mon code en vb.net, seul la balise <Signature> a le préfixe ds, quand j'avais voulu l'ajouter à chaque sous nœud, la signature n'était plus valide.
Qu'est-ce qui doit être changé afin de rendre la signature valide dans le fichier XML?
Merci
Partager