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 :
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
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
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));
 
    }
 
}
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 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