Methods called by XSLT stylesheets for XML Signature processing

The code shown below contains methods called by the XSLT stylesheets used to create and verify XML Signatures. Those experimental stylesheets and this experimental code were written for the January 2000 face-to-face meeting of the XML Signature WG.

Here are some links to methods called by the XSLT stylesheets:

/*
    This file contains the methods called by the XSLT stylesheets that
    for creating and verifying XML Signatures.

    Author: Ed Simon, Entrust Technologies
    
    Note: This code was produced, much of it at the last minute, for the 
    January 2000 meeting of the XML Signature WG.  Hence it is neither
    complete, optimized, well-documented, adequately self-reviewed, nor
    adequately peer-reviewed (though I do extend my thanks to those who
    were able to do a quick review for me).  The sole purpose of this code
    is to be a prototype of how XML Signature code might be used by 
    applications.
*/

import java.io.*;
import java.net.*;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;

import com.ibm.xml.parsers.*;

import com.ibm.xml.dsig.*;
import com.ibm.dom.util.CanonicalizerVisitor;

import com.lotus.xsl.*;

import org.w3c.dom.*;

public class MySecurity {
    
    public static void main(String[] args) {
    
        // used for testing
    }
    
    public static String test() {
        return "TEST";
    }

    // Takes a string as input and applies the transforms (as indicated by
    // the content and order of the Transform elements) to the input,
    // and returns the result.
    private static String doTransforms(String strInput, NodeList nlTransforms) {
                    
        Node nodeTransforms = (Node) nlTransforms.item(0);
        
        NodeList nlTransform = nodeTransforms.getChildNodes();
            
        for (int i = 0; i < nlTransform.getLength(); i++) {
                
            Node node = nlTransform.item(i);
                
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                    
                Element elemTransform = (Element) nlTransform.item(i);                    

                String strTransformAlgorithm = elemTransform.getAttribute("Algorithm");
                
                // Add other "else if"s to support other transforms
                
                // Base64 encoding 
                if (strTransformAlgorithm.equals("http://www.w3.org/2000/01/xmldsig/base64")) {
                        
                    strInput = Base64.encode(strInput.getBytes());
                        
                // W3C XML Canonicalizaton
                } else if (strTransformAlgorithm.equals("http://www.w3.org/1999/07/WD-xml-c14n-19990729")) {
                    try {        
                        
                        strInput =     getCanonicalXML(strInput);
                        
                    } catch (Exception ex) {
                        System.out.println("Exception while transforming: " + ex);
                    }
                // XSLT stylesheets
                } else if (strTransformAlgorithm.equals("http://www.w3.org/TR/1999/REC-xslt-19991116")) {
                        
                    // We use the LotusXSL processor API to apply the stylesheet, see the LotusXSL API
                    // doc for details.
                    try {
                        
                        XSLProcessor xslprocessor = new com.lotus.xsl.XSLProcessor();    
                                                
                        ByteArrayInputStream bytesisInput = new ByteArrayInputStream(strInput.getBytes());
                        
                        XSLTInputSource xsltisInput = new XSLTInputSource(bytesisInput);

                        Node nodeXSLT = node.getChildNodes().item(0);    
                        
                        XSLTInputSource xsltisXSLT = new XSLTInputSource(nodeXSLT);
                        
                        StringWriter sw = new StringWriter();
                        
                        XSLTResultTarget xlstrt = new XSLTResultTarget(sw);
                        
                        xslprocessor.setQuietConflictWarnings(true);
                        
                        xslprocessor.process(xsltisInput, xsltisXSLT, xlstrt);
                                
                        xslprocessor.reset();
                        
                        strInput = sw.getBuffer().toString();
                        
                        PrintWriter pw = null;
                                            
                        try {
                            File out = new File("output_from_xslt.xml");
                            FileWriter fw = new FileWriter(out);
                            pw = new PrintWriter(fw, true);
                        }
                        catch (IOException e) {
                            System.err.println(e.toString());
                            System.exit(1);
                        }        
                                            
                        pw.print(strInput);
                                            
                        pw.close();

                    }
                    catch(Exception exc) {
                        System.out.println("<error>LotusXSL threw an exception: "
                                    +exc.getMessage()+"</error>");
                    }
                
                // The infamous null transform
                } else if (strTransformAlgorithm.equals("http://www.w3.org/2000/01/xmldsig/null")) {
                    // do nothing
                }
            }    
        }
        
        return strInput;
    }
    
    // Call IBM's XML Canonicalization algorithm in their XSS4J Toolkit.
    private static String getCanonicalXML(String strInput) {
        
        StringBuffer strbufOutput = new StringBuffer();
                    
        PrintWriter pw = null;
                            
        try {
            File out = new File("temp_for_transformation_c14n_input.xml");
            FileWriter fw = new FileWriter(out);
            pw = new PrintWriter(fw, true);
        }
        catch (IOException e) {
            System.err.println(e.toString());
            System.exit(1);
        }        
                            
        pw.print(strInput);
                            
        pw.close();

        DOMParser domParser = new DOMParser();
                        
        try {
            domParser.parse("temp_for_transformation_c14n_input.xml");
        } catch (Exception ex) {
            System.out.println("Exception while parsing: " + ex);
        }

        try {
            // The next line is only for DOM Parsers
            Document doc = domParser.getDocument();
                                                                 
            Node node = (Node) doc;
                                
            Infoset infoset = new Infoset(node);
                            
            Canonicalizer canonicalizer = Canonicalizer.getInstance(Canonicalizer.W3C);
                            
            FileOutputStream fileXmlOutputStream = new FileOutputStream("temp_for_transformation_c14n_output.xml");
                                            
            canonicalizer.canonicalize(infoset, fileXmlOutputStream);
                                
            fileXmlOutputStream.flush();
            fileXmlOutputStream.close();
                                

            FileReader fileXmlInput = new FileReader("temp_for_transformation_c14n_output.xml");
            BufferedReader bin = new BufferedReader(fileXmlInput);
                                
            String line;
                                
            while( (line = bin.readLine()) != null) {
                strbufOutput.append(line);
            }
                                
            fileXmlInput.close();
            
        } catch (Exception ex) {
            
            System.out.println("Exception while doing XML Canonicalization: " + ex);
        }
        
        return strbufOutput.toString();
    }
    
    // Get the SHA-1 digest of a string
    private static String getDigest(String strInput) {
        
        DigestMethod digmeth = DigestMethod.getInstance(DigestMethod.SHA1);
        
        digmeth.write(strInput.getBytes());
        
        byte[] digestBytes = digmeth.getDigest();

        String base64ofDigest = Base64.encode(digestBytes);

        return base64ofDigest;
    }
    
    /*
        
        Gets the digest of a URI
    */
    public static String getSha1HashOfUri(String uri, NodeList nlTransforms) {
        
        StringBuffer strbufInput = new StringBuffer();
        
        try {
            URL url = new URL(uri);
            
            BufferedReader bin = new BufferedReader( new InputStreamReader(url.openStream() ));
            
            String line;
            
            while( (line = bin.readLine()) != null) {
                strbufInput.append(line);
            }
            
        } catch (Exception ex) {
            
            System.out.println("Exception: " + ex);
        }

        String strInput = strbufInput.toString();

        if (nlTransforms != null) {
            
            strInput = doTransforms(strInput, nlTransforms);
        }
    
        return getDigest(strInput);
    }

    
    /*
        
        Gets the hash of a URI.
        
        Note: I may not have finished this code in time for the F2F.
    */
    public static String getSha1HashOfIdref(NodeList nlOfReferencedElement, NodeList nlTransforms) {

        Node nodeOfReferencedElement = nlOfReferencedElement.item(0);
        
        Element elReference = (Element) nodeOfReferencedElement;
        
        String strInput = new String();
        
        if (nlTransforms != null) {
            
            strInput = doTransforms(strInput, nlTransforms);
        }
    
        return getDigest(strInput);
    }

    // Utility function to get text nodes within an element.
    private static String getTextOfElement(Element el) {
        
        StringBuffer strbuf = new StringBuffer();
        
        NodeList nl = el.getChildNodes();
        
        for (int i = 0; i < nl.getLength(); i++) {
            
            Node node = nl.item(i);
            
            if (node.getNodeType() == Node.TEXT_NODE) {
                
                strbuf.append(node.getNodeValue());
            }
           
        }
        
        return strbuf.toString();
    }


    /*
        
        Gets the encoded value of a public key.
    */
    public static String getEncodedPublicKey() {
        
        KeyPair keypair = null;
        try {            
            FileInputStream fileinputstream = new FileInputStream("Carl.KeyPair");
            ObjectInputStream objectinputstream = new ObjectInputStream(fileinputstream);
            keypair = (KeyPair) objectinputstream.readObject();
            objectinputstream.close();            
            fileinputstream.close();
            
        } catch (Exception ex) {
            return "Exception: " + ex;
        }

        byte[] bytesPublicKey = keypair.getPublic().getEncoded();
        
        String base64ofPublicKey = Base64.encode(bytesPublicKey);
        
        return base64ofPublicKey;
    }
    
    /*
        
        Return the DSA signature value of the SignedInfo element (after applying XML canonicalization)
    */
    public static String getDsaSignatureValueUsingXmlCanonicalization(NodeList nl) {

        String base64ofSignatureValue = null;
        
        try {        
            
            Node node = (Node) nl.item(0);
            
            Infoset infoset = new Infoset(node);
        
            Canonicalizer canonicalizer = Canonicalizer.getInstance(Canonicalizer.W3C);
        
            FileOutputStream fileXmlOutputStream = new FileOutputStream("CanonicalVersion_sign.xml");
                        
            canonicalizer.canonicalize(infoset, fileXmlOutputStream);
            
            fileXmlOutputStream.flush();
            fileXmlOutputStream.close();
            
            SignatureMethod signatureMethod = SignatureMethod.getInstance(SignatureMethod.DSA);
            KeyPair keypair = null;
            
            FileInputStream fileKeyPairInputStream = new FileInputStream("Carl.KeyPair");
            ObjectInputStream objectinputstream = new ObjectInputStream(fileKeyPairInputStream);
            keypair = (KeyPair) objectinputstream.readObject();
            objectinputstream.close();            
            fileKeyPairInputStream.close();

            signatureMethod.initSign(keypair.getPrivate());

            FileReader fileXmlInput = new FileReader("CanonicalVersion_sign.xml");
            BufferedReader bin = new BufferedReader(fileXmlInput);
            
            StringBuffer strbufOutput = new StringBuffer();
            String line;
            
            while( (line = bin.readLine()) != null) {
                strbufOutput.append(line);
            }
            
            fileXmlInput.close();
            
            signatureMethod.update(strbufOutput.toString().getBytes());
        
            byte[] bytesSignatureValue = signatureMethod.sign();
        
            base64ofSignatureValue = Base64.encode(bytesSignatureValue);
            
        } catch (Exception ex) {
            return "Exception: " + ex;
        }
        
        return base64ofSignatureValue;

    }


    /*
        
        Verify the DSA signature in a SignatureValue element.
    */
    public static String verifyDsaSignatureValueUsingXmlCanonicalization(NodeList nl, 
                                                                          String strInputSignatureValue,
                                                                          String strPublicKeyValue) {

        boolean boolValidityOfSignedInfoElement = false;
        
        String base64ofSignatureValue = null;
        
        try {        
            
            Node node = (Node) nl.item(0);
            
            Infoset infoset = new Infoset(node);
        
            Canonicalizer canonicalizer = Canonicalizer.getInstance(Canonicalizer.W3C);
        
            FileOutputStream fileXmlOutputStream = new FileOutputStream("CanonicalVersion_verify.xml");
                        
            canonicalizer.canonicalize(infoset, fileXmlOutputStream);
            
            fileXmlOutputStream.flush();
            fileXmlOutputStream.close();
            
            SignatureMethod signatureMethod = SignatureMethod.getInstance(SignatureMethod.DSA);
            
            byte[] bytesPublicKey = Base64.decode(strPublicKeyValue);
            
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(bytesPublicKey);

            KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
            
            signatureMethod.initVerify(pubKey);

            FileReader fileXmlInput = new FileReader("CanonicalVersion_verify.xml");
            BufferedReader bin = new BufferedReader(fileXmlInput);
            
            StringBuffer strbufOutput = new StringBuffer();
            String line;
            
            while( (line = bin.readLine()) != null) {
                strbufOutput.append(line);
            }
            
            fileXmlInput.close();
            
            signatureMethod.update(strbufOutput.toString().getBytes());
            
            byte[] bytesOfSignatureValue = Base64.decode(strInputSignatureValue.trim());
        
            boolValidityOfSignedInfoElement = signatureMethod.verify(bytesOfSignatureValue);    
            
        } catch (Exception ex) {
            return "Exception: " + ex;
        }

        
        if (!boolValidityOfSignedInfoElement) {                            
            return "NOT VALID";
        }        
        
        return "VALID";    

    }
}