Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8017171

ID-references unresolvable in XML signature validation

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version " 1.7.0_25 "
      Java(TM) SE Runtime Environment (build 1.7.0_25-b16)
      Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Windows 7 Professional, 64-bit

      A DESCRIPTION OF THE PROBLEM :
      XML signature validation code that functions in jre170_21 and earlier, breaks in the latest release. This pertains to signatures for manifest items, referenced by xmldsig:id attributes in the XML document.

      The XML signature validation source code is available here:
      https://raw.github.com/joval/jOVAL/master/src/org/joval/xml/SignatureValidator.java

      A Java keystore file containing the NIST certificate is available here:
      https://github.com/joval/jOVAL/raw/master/components/jovaldi/rsrc/security/cacerts.jks

      I am attempting to validate the XML documents contained here (specifically scap_gov.nist_USGCB-Windows-7.xml):
      http://usgcb.nist.gov/usgcb/content/scap/oval510/Win7-510-1.2.7.1.zip


      REGRESSION. Last worked in version 6u45

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Instantiate an org.joval.xml.SignatureValidator with the XML file and keystore backed by the file I referenced above, and invoke the validate() method.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The method should return true.
      ACTUAL -
      An XMLSignatureException is thrown.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      2013.06.19 22:46:16.588 - INFO - Verifying XML digital signature: ..\..\..\..\content\scap12\scap_gov.nist_USGCB-Windows-7.xml
      2013.06.19 22:46:17.334 - SEVERE - javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID scap_gov.nist_datastream_USGCB-Windows-7-1.2.3.1.zip
          org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:412)
          org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java:371)
          org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate(DOMXMLSignature.java:265)
          org.joval.xml.SignatureValidator.validate(SignatureValidator.java:104)
          org.joval.scap.xccdf.engine.XPERT.main(XPERT.java:283)
      caused by:
      javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID scap_gov.nist_datastream_USGCB-Windows-7-1.2.3.1.zip
          org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:124)
          org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:404)
          org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java:371)
          org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate(DOMXMLSignature.java:265)
          org.joval.xml.SignatureValidator.validate(SignatureValidator.java:104)
          org.joval.scap.xccdf.engine.XPERT.main(XPERT.java:283)
      caused by:
      com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID scap_gov.nist_datastream_USGCB-Windows-7-1.2.3.1.zip
          com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverFragment.engineResolve(ResolverFragment.java:90)
          com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.resolve(ResourceResolver.java:283)
          org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:117)
          org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:404)
          org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java:371)
          org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate(DOMXMLSignature.java:265)
          org.joval.xml.SignatureValidator.validate(SignatureValidator.java:104)
          org.joval.scap.xccdf.engine.XPERT.main(XPERT.java:283)


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      // Copyright (C) 2012 jOVAL.org. All rights reserved.
      // This software is licensed under the AGPL 3.0 license available at http://www.joval.org/agpl_v3.txt

      package org.joval.xml;

      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.util.Enumeration;
      import java.util.NoSuchElementException;
      import java.security.Key;
      import java.security.KeyException;
      import java.security.KeyStore;
      import java.security.KeyStoreException;
      import java.security.PublicKey;
      import java.security.cert.Certificate;
      import java.security.cert.CertificateFactory;
      import java.security.cert.CertSelector;
      import java.security.cert.X509Certificate;
      import java.security.cert.X509CertSelector;
      import javax.security.auth.x500.X500Principal;
      import javax.xml.XMLConstants;
      import javax.xml.crypto.AlgorithmMethod;
      import javax.xml.crypto.KeySelector;
      import javax.xml.crypto.KeySelectorException;
      import javax.xml.crypto.KeySelectorResult;
      import javax.xml.crypto.MarshalException;
      import javax.xml.crypto.NoSuchMechanismException;
      import javax.xml.crypto.NodeSetData;
      import javax.xml.crypto.OctetStreamData;
      import javax.xml.crypto.XMLCryptoContext;
      import javax.xml.crypto.XMLStructure;
      import javax.xml.crypto.dsig.SignatureMethod;
      import javax.xml.crypto.dsig.XMLSignature;
      import javax.xml.crypto.dsig.XMLSignatureException;
      import javax.xml.crypto.dsig.XMLSignatureFactory;
      import javax.xml.crypto.dsig.dom.DOMValidateContext;
      import javax.xml.crypto.dsig.keyinfo.KeyInfo;
      import javax.xml.crypto.dsig.keyinfo.KeyName;
      import javax.xml.crypto.dsig.keyinfo.KeyValue;
      import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
      import javax.xml.crypto.dsig.keyinfo.X509Data;
      import javax.xml.crypto.dsig.keyinfo.X509IssuerSerial;
      import javax.xml.parsers.DocumentBuilderFactory;
      import javax.xml.parsers.FactoryConfigurationError;
      import javax.xml.parsers.ParserConfigurationException;
      import javax.xml.transform.Source;
      import javax.xml.transform.dom.DOMSource;
      import org.xml.sax.SAXException;
      import org.w3c.dom.Document;
      import org.w3c.dom.NodeList;

      /**
       * Utility class for validating XML digital signatures.
       * @see http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/XMLDigitalSignatureAPI8.html
       *
       * @author David A. Solin
       * @version %I% %G%
       */
      public class SignatureValidator {
          private Document doc;
          private NodeList signatures;
          private KeyStore keyStore;

          /**
           * Create a new validator for the specified XML file using the specified KeyStore.
           */
          public SignatureValidator(File f, KeyStore keyStore)
      throws FactoryConfigurationError, ParserConfigurationException, SAXException, IOException {

      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware(true);
      doc = dbf.newDocumentBuilder().parse(f);
      signatures = doc.getElementsByTagNameNS(XMLSignature.XMLNS, " Signature " );
      this.keyStore = keyStore;
          }

          /**
           * Assign a specific KeyStore to be used for validation.
           */
          public void setKeyStore(KeyStore keyStore) {
      this.keyStore = keyStore;
          }

          /**
           * Returns whether or not there is an XML signature node in the document.
           */
          public boolean containsSignature() {
      return signatures.getLength() > 0;
          }

          /**
           * Validate the document's XML digital signature.
           */
          public boolean validate() throws XMLSignatureException {
      int len = signatures.getLength();
      if (len == 0) {
          throw new XMLSignatureException( " Cannot find Signature element " );
      } else {
          try {
      for (int i=0; i < len; i++) {
          DOMValidateContext ctx = new DOMValidateContext(new X509KeySelector(), signatures.item(i));
          XMLSignature signature = XMLSignatureFactory.getInstance().unmarshalXMLSignature(ctx);
          if (!signature.validate(ctx)) {
      return false;
          }
      }
      return true;
          } catch (NoSuchMechanismException e) {
      throw new XMLSignatureException(e);
          } catch (ClassCastException e) {
      throw new XMLSignatureException(e);
          } catch (MarshalException e) {
      throw new XMLSignatureException(e);
          }
      }
          }

          /**
           * Validate the document's XML digital signature using the supplied public key.
           */
          public boolean validate(Key validationKey) throws XMLSignatureException {
      int len = signatures.getLength();
      if (len == 0) {
          throw new XMLSignatureException( " Cannot find Signature element " );
      } else {
          try {
      for (int i=0; i < len; i++) {
          DOMValidateContext ctx = new DOMValidateContext(validationKey, signatures.item(i));
          XMLSignature signature = XMLSignatureFactory.getInstance().unmarshalXMLSignature(ctx);
          if (!signature.validate(ctx)) {
      return false;
          }
      }
      return true;
          } catch (NoSuchMechanismException e) {
      throw new XMLSignatureException(e);
          } catch (ClassCastException e) {
      throw new XMLSignatureException(e);
          } catch (MarshalException e) {
      throw new XMLSignatureException(e);
          }
      }
          }

          /**
           * Obtain a Source for the document. This makes it possible to insure that the underlying data has been validated.
           */
          public Source getSource() {
      return new DOMSource(doc);
          }

          // Private

          /**
           * KeySelector implementation for X.509 certificates.
           */
          class X509KeySelector extends KeySelector {
      @Override
      public KeySelectorResult select(KeyInfo ki, KeySelector.Purpose p, AlgorithmMethod am, XMLCryptoContext ctx)
      throws KeySelectorException {

          KeySelectorResult result = null;
          SignatureMethod sm = (SignatureMethod)am;
          for (Object obj : ki.getContent()) {
      XMLStructure xs = (XMLStructure)obj;
      if (xs instanceof X509Data) {
          result = select((X509Data)xs, sm);
      } else if (xs instanceof KeyName) {
          KeyName kn = (KeyName)xs;
          try {
      Certificate cert = keyStore.getCertificate(kn.getName());
      if (cert != null && equals(sm.getAlgorithm(), cert.getPublicKey().getAlgorithm())) {
          result = new SimpleKeySelectorResult(cert.getPublicKey());
      }
          } catch (KeyStoreException e) {
      throw new KeySelectorException(e);
          }
      } else if (xs instanceof RetrievalMethod) {
          RetrievalMethod rm = (RetrievalMethod)xs;
          try {
      if (rm.getType().equals(X509Data.RAW_X509_CERTIFICATE_TYPE)) {
          OctetStreamData data = (OctetStreamData)rm.dereference(ctx);
          CertificateFactory cf = CertificateFactory.getInstance( " X.509 " );
          X509Certificate cert = (X509Certificate)cf.generateCertificate(data.getOctetStream());
          result = select(cert, sm);
      } else if (rm.getType().equals(X509Data.TYPE)) {
          NodeSetData nd = (NodeSetData)rm.dereference(ctx);
          System.out.println( " DAS conversion is TBD " );
      }
          } catch (Exception e) {
      throw new KeySelectorException(e);
          }
      } else if (xs instanceof KeyValue) {
          PublicKey pk = null;
          try {
      pk = ((KeyValue)xs).getPublicKey();
          } catch (KeyException ke) {
      throw new KeySelectorException(ke);
          }
          if (equals(sm.getAlgorithm(), pk.getAlgorithm())) {
      result = new SimpleKeySelectorResult(pk);
          }
      }
      if (result != null) {
          return result;
      }
          }
          throw new KeySelectorException( " Key not found " );
      }

      // Internal

      KeySelectorResult select(X509Data data, SignatureMethod sm) throws KeySelectorException {
          String oid = getOid(sm.getAlgorithm());
          KeySelectorResult result = null;
          for (Object obj : data.getContent()) {
      try {
          if (obj instanceof X509Certificate) {
      return select((X509Certificate)obj, sm);
          } else if (obj instanceof X509IssuerSerial) {
      X509IssuerSerial xis = (X509IssuerSerial)obj;
      X509CertSelector xcs = new X509CertSelector();
      xcs.setSubjectPublicKeyAlgID(oid);
      xcs.setSerialNumber(xis.getSerialNumber());
      xcs.setIssuer(new X500Principal(xis.getIssuerName()).getName());
      return select(xcs);
          } else if (obj instanceof String) {
      String sn = (String)obj;
      X509CertSelector xcs = new X509CertSelector();
      xcs.setSubjectPublicKeyAlgID(oid);
      xcs.setSubject(new X500Principal(sn).getName());
      return select(xcs);
          } else if (obj instanceof byte[]) {
      byte[] ski = (byte[])obj;
      X509CertSelector xcs = new X509CertSelector();
      xcs.setSubjectPublicKeyAlgID(oid);
      byte[] encodedSki = new byte[ski.length+2];
      encodedSki[0] = 0x04; // OCTET STRING tag value
      encodedSki[1] = (byte)ski.length;
      System.arraycopy(ski, 0, encodedSki, 2, ski.length);
      xcs.setSubjectKeyIdentifier(encodedSki);
      return select(xcs);
          }
      } catch (IOException e) {
          throw new KeySelectorException(e);
      } catch (KeyStoreException e) {
          throw new KeySelectorException(e);
      } catch (NoSuchElementException e) {
          throw new KeySelectorException(e);
      }
          }
          return null;
      }

      /**
       * Return the certificate in the key store that matches the specified selector
       *
       * @throws NoSuchElementException if there is no match
       */
      KeySelectorResult select(X509CertSelector cs) throws KeyStoreException, NoSuchElementException {
          for (Enumeration e = keyStore.aliases(); e.hasMoreElements(); ) {
      Certificate cert = keyStore.getCertificate((String)e.nextElement());
      if (cert != null && cs.match(cert)) {
          return new SimpleKeySelectorResult(cert.getPublicKey());
      }
          }
          throw new NoSuchElementException(cs.getSubject().toString());
      }

      /**
       * Return the certificate in the key store that matches the specified X509 certificate (or null if there is no match).
       *
       * @throws NoSuchElementException if there is no match
       */
      KeySelectorResult select(X509Certificate xc, SignatureMethod sm) throws KeyStoreException, NoSuchElementException {
          boolean[] keyUsage = xc.getKeyUsage();
          if (keyUsage[0]) {
      String alias = keyStore.getCertificateAlias(xc);
      if (alias != null) {
          PublicKey pk = keyStore.getCertificate(alias).getPublicKey();
          if (equals(sm.getAlgorithm(), pk.getAlgorithm())) {
      return new SimpleKeySelectorResult(pk);
          }
      }
          }
          throw new NoSuchElementException(xc.getSubjectX500Principal().toString());
      }

      /**
       * Return the OID corresponding to the Algorithm URI.
       */
      String getOid(String uri) {
          if (uri.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
      return " 1.2.840.10040.4.1 " ;
          } else if (uri.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
      return " 1.2.840.113549.1.1 " ;
          } else {
      return null;
          }
      }

      /**
       * Determine whether the algorithm URI matches the algorithm name.
       */
      boolean equals(String uri, String name) {
          if (name.equalsIgnoreCase( " DSA " ) && uri.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
      return true;
          } else if (name.equalsIgnoreCase( " RSA " ) && uri.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
      return true;
          } else {
      return false;
          }
      }
          }

          /**
           * A simple KeySelectorResult implementation.
           */
          static class SimpleKeySelectorResult implements KeySelectorResult {
      private Key key;

      SimpleKeySelectorResult(Key key) {
          this.key = key;
      }

      // Implement KeySelectorResult

      public Key getKey() {
          return key;
      }
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      We will have to distribute our application with an earlier release of Java, such as 170_21.

            mullan Sean Mullan
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: