2015-11-30 1 views
14

У меня возникла проблема, когда я подписываю документ xml и проверяю подпись после этого, а затем проверяю, но когда я сериализую документ в массив байтов, а затем десериализую его обратно в документ, подтверждение подписи выходит из строя.Подпись документа Xml недействительна после десериализации из массива байтов

Вот методы, используемые для проверки и сериализации/десериализации:

public class DocumentSigner { 
    @Override 
    public byte[] transformToByteArray(Document doc) throws Exception { 
     TransformerFactory transformerFactory = TransformerFactory 
       .newInstance(); 
     Transformer transformer = transformerFactory.newTransformer();  
     ByteArrayOutputStream os = new ByteArrayOutputStream(); 
     transformer.transform(new DOMSource(doc), new StreamResult(os)); 
     return os.toByteArray(); 
    } 

    private Document byteArrayToXmlDoc(byte[] documentoXml) throws Exception { 
     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
     factory.setNamespaceAware(true); 
     factory.setIgnoringElementContentWhitespace(true); 
     DocumentBuilder builder = factory.newDocumentBuilder(); 
     return builder.parse(new ByteArrayInputStream(documentoXml), "UTF-8"); 
    } 

    @Override 
    public Boolean validate(byte[] byteArrayDoc, Integer certificatePropertiesId) throws Exception { 
     Document doc = byteArrayToXmlDoc(byteArrayDoc); 
     return validate(doc, certificatePropertiesId); 
    } 

    public Boolean validate(Document doc, Integer certificatePropertiesId) throws Exception { 
     NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, 
       "Signature"); 
     if (nl.getLength() == 0) { 
      throw new Exception("No signature element."); 
     } 

     KeyStore ks = KeyStore.getInstance("JKS"); 
     CertificatePropertiesDTO certProp = databaseLogic.getCertificateProperties(certificatePropertiesId); 
     if (certProp == null || certProp.getCertificatePassword().isEmpty() || certProp.getCertificate() == null){ 
      throw new RuntimeException("No certificate."); 
     } 

     ks.load(new ByteArrayInputStream(certProp.getCertificate()), certProp.getCertificatePassword().toCharArray()); 
     KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(ks.aliases().nextElement(), new KeyStore.PasswordProtection(certProp.getCertificatePassword().toCharArray())); 
     X509Certificate[] certs = (X509Certificate[]) keyEntry.getCertificateChain(); 
     if (certs == null || certs.length == 0) { 
      throw new RuntimeException("No certificate found."); 
     } 

     XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); 
     DOMValidateContext valContext = new DOMValidateContext(keyEntry.getCertificate().getPublicKey(), nl.item(0)); 
     NodeList els = doc.getElementsByTagNameNS("*", "SignatureProperties"); 
     Element el = (Element) els.item(0); 
     valContext.setIdAttributeNS(el, null, "Id"); 
     valContext.setDefaultNamespacePrefix("dsig"); 

     valContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE); 
     try { 
      XMLSignature signature2 = fac 
        .unmarshalXMLSignature(new DOMStructure(nl.item(0))); 
      boolean coreValidity = signature2.validate(valContext); 

      // Check core validation status. 
      if (coreValidity == false) { 
       log.info("Signature failed core validation"); 
       boolean sv = signature2.getSignatureValue() 
         .validate(valContext); 
       log.info("signature validation status: " + sv); 
       Iterator<?> i = signature2.getSignedInfo().getReferences() 
         .iterator(); 
       for (int j = 0; i.hasNext(); j++) { 
        Reference ref = (Reference) i.next(); 
        boolean refValid = ref.validate(valContext); 
        log.info("ref[" + j + "] validity status: " + refValid); 
       } 
       return false; 
      } else { 
       log.info("Signature passed core validation"); 
       return true; 
      } 
     } catch (Exception ex) { 
      log.info("EXCEPTION during validation: " + ex.getMessage()); 
      return false; 
     } 
    } 

    public void signDocument(Document doc) 
    { 
     .... 
    } 



public void writeToDisk(String path, String rac) 
    { 
     BufferedWriter writer = null; 
     try 
     { 
      writer = new BufferedWriter(new FileWriter(path)); 
      writer.write(rac); 

     } 
     catch (IOException e) 
     { 
     } 
     finally 
     { 
      try 
      { 
       if (writer != null) 
       writer.close(); 
      } 
      catch (IOException e) 
      { 
       try { 
        throw e; 
       } catch (IOException e1) { 
        // TODO Auto-generated catch block 
        e1.printStackTrace(); 
       } 
      } 
     } 
    } 

    @Override 
    public String transformToString(Document doc, 
      Boolean omitXmlDeclaration) throws Exception { 
     TransformerFactory transformerFactory = TransformerFactory 
       .newInstance(); 
     //transformerFactory.setAttribute("indent-number", 4); 
     Transformer transformer = transformerFactory.newTransformer();  
     if (omitXmlDeclaration) 
      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, 
        "yes"); 
//  transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
//  transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
//    
     StringWriter sw = new StringWriter(); 
     transformer.transform(new DOMSource(doc), new StreamResult(sw)); 
     //String output = sw.getBuffer().toString().replaceAll("\n|\r", ""); 
     return sw.toString(); 
    } 
} 

Вот где она проходит/не удается:

public void SignAndValidate() 
{ 
    ... 
    Document doc = createDocument(); 
    documentSigner.signDocument(doc); 

    validate(doc, 1); 

    // OUTPUT: 
    // Signature passed core validation 

    byte[] docArr = documentSigner.transformToByteArray(doc); 

    validate(docArr, 1); 

    // OUTPUT: 
    // Signature failed core validation 
    // signature validation status: false 
    // ref[0] validity status: false 
    // ref[1] validity status: true 
} 

В случае необходимости я буду размещать методы создания/подписания документа, но оно большое.

Вот метод подписи:

Вот часть документа образец XML, который подписан:

 <ext:UBLExtension> 
      <ext:ExtensionContent> 
       <sig:UBLDocumentSignatures> 
        <sac:SignatureInformation> 
         <dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Id="data_signature"> 
          <dsig:SignedInfo> 
           <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments"/> 
           <dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> 
           <dsig:Reference Type="http://www.w3.org/2000/09/xmldsig#SignatureProperties" URI="#idfe5688f4-583f-4a98-b26c-9d651b2f8918"> 
            <dsig:Transforms> 
             <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments"/> 
            </dsig:Transforms> 
            <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> 
            <dsig:DigestValue>iq802IBHl5kVdIMWA5Wlb5hYEoY=</dsig:DigestValue> 
           </dsig:Reference> 
           <dsig:Reference URI=""> 
            <dsig:Transforms> 
             <dsig:Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2"> 
              <dsig:XPath Filter="intersect" xmlns:dsig="http://www.w3.org/2002/06/xmldsig-filter2">here()/ancestor::dsig:Signature[1]/../../../../../..//. | here()/ancestor::dsig:Signature[1]/../../../../../..//@* | here()/ancestor::dsig:Signature[1]/../../../../../..//namespace::*</dsig:XPath> 
             </dsig:Transform> 
             <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> 
             <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments"/> 
            </dsig:Transforms> 
            <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> 
            <dsig:DigestValue>2jmj7l5rSw0yVb/vlWAYkK/YBwk=</dsig:DigestValue> 
           </dsig:Reference> 
          </dsig:SignedInfo> 
          <dsig:SignatureValue>d+DRc25SXnhxwXJs10A9Hnf1g0gG2bZqqnpTbZvrzp8X3EvtOVr3dBP6Ldc1RMTJYSF+guanlWKn 
liaKlu7VbdB+SiQRuAMAZt+9Cnbn0CMlIzt22nRJNzjbeLBpCm7K63jCHGOXsWCW43DI/DYeZwq+ 
Q2j7WESgOtWLqUO0Jn8=</dsig:SignatureValue> 
          <dsig:KeyInfo> 
           <dsig:X509Data> 
            <dsig:X509Certificate>...</dsig:X509Certificate> 
            <dsig:X509Certificate>...</dsig:X509Certificate> 
           </dsig:X509Data> 
          </dsig:KeyInfo> 
          <dsig:Object> 
           <dsig:SignatureProperties Id="idfe5688f4-583f-4a98-b26c-9d651b2f8918"> 
            <dsig:SignatureProperty Target="data_signature"> 
             <PROP_Sig xmlns="http://ns.adobe.com/pdf/2006" type="cabinet"> 
              <M type="text">D:20151130163741+0100</M> 
              <Name type="text">CN=<CN>,L=<City>,O=<Organization>,C=<Country></Name> 
             </PROP_Sig> 
            </dsig:SignatureProperty> 
           </dsig:SignatureProperties> 
          </dsig:Object> 
         </dsig:Signature> 
        </sac:SignatureInformation> 
       </sig:UBLDocumentSignatures> 
      </ext:ExtensionContent> 
     </ext:UBLExtension> 
    </ext:UBLExtensions> 

Я не понимаю, почему проверка говорит ссылки [0] не удается (тот, который ссылается на элемент с id), но ссылка на весь документ проходит?

+0

Не могли бы вы попытаться удалить 'factory.setIgnoringElementContentWhitespace (true);' в 'byteArrayToXmlDoc'? Это может вызвать проблему. Проблема заключается в том, что ваши преобразования XML из/в байтовые массивы не являются обратными операциями. – vojta

+0

@vojta Это не помогло, точно такой же результат. Вероятно, он остался в коде случайно, так как я играл с разными вариантами до публикации, надеясь, что это поможет. – formatc

+0

Пожалуйста, опубликуйте представления 'String' вашего XML-документа перед преобразованием в массив' byte' и после преобразования обратно в 'Document' в ваш' validate (byte [] byteArrayDoc, ...) 'метод. Должно быть заметное различие. (XML to String: http://stackoverflow.com/questions/5456680/xml-document-to-string) – vojta

ответ

2

@formatc У меня нет привилегий для комментариев, но вы можете попробовать и просмотреть шестнадцатеричные значения в обоих файлах (знак и десериализация). У меня была та же проблема, по какой-то причине в моем случае при построении назад xml перед документом вставлены некоторые невизуальные символы. Вы не увидите их, если не используете HexView или какой-нибудь инструмент.

Я был в состоянии удалить эти символы программно, и все прошло хорошо.

+0

Я проверил оба с шестнадцатеричным редактором, они такие же, по крайней мере на диске. Спасибо за предложение в любом случае. – formatc

0
  • Убедитесь, что длина массива символов для корпуса равна им? перед проверкой вы должны увидеть некоторые различия.
  • Кроме того, некоторые технологии Signture могут использовать любые внутренние префиксы знаков, такие как GUID, управлять ими.

Используйте utf8 как на

0

я была точно такая же проблема, как у вас есть. подпись действительна, но ссылки не указаны. проблема (по крайней мере, в моем случае), что сериализация и десериализация могут влиять на содержимое xml. в моем случае он помог позвонить document.normalizeDocument() перед подписанием документа, и теперь подпись проверяется даже после сериализации/десериализации.

0

В моем случае это была разница в значении заголовка, поэтому он терпел неудачу.

оригинальный XML-документ имеет и когда он был написан заголовок был изменен на

Именно поэтому проверка подписи терпел неудачу.

Следовательно, удалите объявление xml при разборе документа и при написании документа.

Смежные вопросы