United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6954275 XML signatures with reference data larger 16KB and cacheRef on fails to validate
JDK-6954275 : XML signatures with reference data larger 16KB and cacheRef on fails to validate

Details
Type:
Bug
Submit Date:
2010-05-20
Status:
Closed
Updated Date:
2012-10-24
Project Name:
JDK
Resolved Date:
2011-03-07
Component:
security-libs
OS:
windows_xp
Sub-Component:
javax.xml.crypto
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
6u10
Fixed Versions:

Related Reports
Duplicate:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Client VM (build 16.3-b01, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
all (Java code bug)

EXTRA RELEVANT SYSTEM CONFIGURATION :
  Buggy java code is
com/sun/org/apache/xml/internal/security/utils/UnsyncByteArrayOutputStream
(rt.jar / src.zip)

A DESCRIPTION OF THE PROBLEM :
Java code

com/sun/org/apache/xml/internal/security/utils/UnsyncByteArrayOutputStream

expands the internal byte[] buf; with help of function void expandSize(); which does not know the real size to use; it enlarges the buffer size by factor 2. If on write access the actual size is too small but larger than the result of applying factor 2 the ArrayOutOfBoundsException raises on a call to

System.arraycopy()

with invalid bounds. Bug in functions public void write(byte[] arg0);
and public void write(byte[] arg0, int arg1, int arg2);



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Verifying an XML signature (reference.validate()) with data referenced larger than 16KB and cacheReference set to true raises an ArrayOutOfBoundsException.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Retrieval of referenced data.
ACTUAL -
ArrayOutOfBoundsException

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package xmldsigbug;

import java.io.File;
import java.security.Key;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.XMLSignature;
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.X509Data;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/** sample program to demonstrate bug in UnsyncByteArrayOutputStream.java
 * Java: 1.6.0_20 and earlier; xml-Security-1.4.3 and earlier
 *
 * UnsyncByteArrayOutputStream is triggered by a caching version of
 * DigesterOutputStream and used if javax.xml.crypto.dsig.cacheReference
 * is set to true. The bug 'ArrayIndexOutOfBoundException' happens
 * when reference.validate() is called.
 *
 * @author a.menke
 */
public class Main
{
  public static final String TESTFILE = "xmlSignatureWithReferenceDataLargerThan16kb.xml";
  /**
   * @param args the command line arguments
   */
  public static void main( String[] args )
  {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware( true );

    try
    {
      File file = new File(TESTFILE);
      
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document doc = db.parse( file );

      XMLSignatureFactory facXMLSignature = XMLSignatureFactory.getInstance( "DOM" );

//Code Sample 5
// Find Signature element.
      NodeList nlSignatures = doc.getElementsByTagNameNS( XMLSignature.XMLNS, "Signature" );
      if( nlSignatures.getLength() == 0 )
      {
        throw new Exception( "Cannot find Signature element" );
      }

      for( int ii = 0; ii < nlSignatures.getLength(); ++ii )
      {
// Create a DOMValidateContext and specify a KeySelector
// and document context.
        DOMValidateContext valContext = new DOMValidateContext( new X509KeySelector(), nlSignatures.item( ii ) );
        valContext.setBaseURI( "file:///" + file.getAbsolutePath().replace( '\\', '/' ).replace( ' ', '+' ) );

//Code Sample 7
// if you disable the following line all works fine: caching disabled
        valContext.setProperty( "javax.xml.crypto.dsig.cacheReference", Boolean.TRUE );
// Unmarshal the XMLSignature.
        XMLSignature signature = facXMLSignature.unmarshalXMLSignature( valContext );
// Validate the XMLSignature.
// not neccesary
//        boolean coreValidity = signature.validate( valContext );

        Iterator itReferences = signature.getSignedInfo().getReferences().iterator();
        for( int jj = 0; itReferences.hasNext(); ++jj )
        {
          Reference reference = (Reference)itReferences.next();
// here we get get the bug if cacheReference is enabled and the referenced data is larger than 2*8*1024byte
          boolean refValid = reference.validate( valContext );
        }
      }
    }
    catch( Exception ex )
    {
      Logger.getLogger( Main.class.getName() ).log( Level.SEVERE, null, ex );
    }
  }
    /* Code Samples from
   * http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/
   */
  // Code Sample 8
  public static class X509KeySelector extends KeySelector
  {
    public KeySelectorResult select( KeyInfo keyInfo,
                                     KeySelector.Purpose purpose,
                                     AlgorithmMethod method,
                                     XMLCryptoContext context )
            throws KeySelectorException
    {
      if( null == keyInfo || null == purpose || null == method || null == context )
      {
        return null;
      }

      /** should iterate through keyInfo and
       * MUST preselsect the public key without
       * further knowledge of the signature
       */
      Iterator ki = keyInfo.getContent().iterator();
      while( ki.hasNext() )
      {
        XMLStructure info = (XMLStructure) ki.next();
        if( !(info instanceof X509Data) )
          continue;
        X509Data x509Data = (X509Data) info;
        Iterator xi = x509Data.getContent().iterator();
        while( xi.hasNext() )
        {
          Object o = xi.next();
          if( !(o instanceof X509Certificate) )
            continue;
          final PublicKey key = ((X509Certificate) o).getPublicKey();
          // Make sure the algorithm isReference compatible
          // with the method.
          if( algEquals( method.getAlgorithm(), key.getAlgorithm() ) )
          {
            return new KeySelectorResult()
            {
              @Override
              public Key getKey()
              {
                return key;
              }
            };
          }
        }
      }
      throw new KeySelectorException( "No key found!" );
    }

    static boolean algEquals( String algURIMethod, String algNameKey )
    {
      if(   (algNameKey.equalsIgnoreCase( "DSA" )
             && algURIMethod.equalsIgnoreCase( SignatureMethod.DSA_SHA1 ))
         || (algNameKey.equalsIgnoreCase( "RSA" )
             && (algURIMethod.equalsIgnoreCase( SignatureMethod.RSA_SHA1 )
                 || algURIMethod.equalsIgnoreCase( "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" )
                )
            )
        )
      {
        return true;
      }
      else
      {
        return false;
      }
    }
  }
}

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

CUSTOMER SUBMITTED WORKAROUND :
Workaround:
Using setURIDereferencer() with an own implementation of an URIDereferencer without that problem.


FIX:

A good fix is hard to decide: it might be that caching is a question of design.
A quick fix with a correct byte buffer might exhaust memory on large sizes.
A better approach might be the use of a user supplied stream object.
Another approach questionaire an other architecture where the 'hack' on
DigesterOutputStream to cache the data is superflous

                                    

Comments
EVALUATION

Yes, there is a buffer overflow bug in com/sun/org/apache/xml/internal/security/utils/UnsyncByteArrayOutputStream.java
                                     
2010-10-15



Hardware and Software, Engineered to Work Together