/* * Copyright(c) 2002 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the Academic Free License Version 1.0 * * Academic Free License * Version 1.0 * * This Academic Free License applies to any software and associated * documentation (the "Software") whose owner (the "Licensor") has placed the * statement "Licensed under the Academic Free License Version 1.0" immediately * after the copyright notice that applies to the Software. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of the Software (1) to use, copy, modify, merge, publish, perform, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, and (2) under patent * claims owned or controlled by the Licensor that are embodied in the Software * as furnished by the Licensor, to make, use, sell and offer for sale the * Software and derivative works thereof, subject to the following conditions: * * - Redistributions of the Software in source code form must retain all * copyright notices in the Software as furnished by the Licensor, this list * of conditions, and the following disclaimers. * - Redistributions of the Software in executable form must reproduce all * copyright notices in the Software as furnished by the Licensor, this list * of conditions, and the following disclaimers in the documentation and/or * other materials provided with the distribution. * - Neither the names of Licensor, nor the names of any contributors to the * Software, nor any of their trademarks or service marks, may be used to * endorse or promote products derived from this Software without express * prior written permission of the Licensor. * * DISCLAIMERS: LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE SOFTWARE IS * OWNED BY THE LICENSOR OR THAT THE SOFTWARE IS DISTRIBUTED BY LICENSOR UNDER * A VALID CURRENT LICENSE. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY * PRECEDING SENTENCE, THE SOFTWARE IS PROVIDED BY THE LICENSOR, CONTRIBUTORS * AND COPYRIGHT OWNERS "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE * LICENSOR, CONTRIBUTORS OR COPYRIGHT OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE. * * This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. * Permission is hereby granted to copy and distribute this license without * modification. This license may not be modified without the express written * permission of its copyright owner. */ /* ===== * * $Header: /home/cvsroot/ebxml-pkg/src/hk/hku/cecid/ebms/pkg/PKISignatureImpl.java,v 1.1 2005/07/28 09:36:24 dcmsze Exp $ * * Code authored by: * * cyng [2002-05-22] * * Code reviewed by: * * username [YYYY-MM-DD] * * Remarks: * * ===== */ package hk.hku.cecid.ebms.pkg; import hk.hku.cecid.ebms.pkg.pki.ApacheXMLDSigner; import hk.hku.cecid.ebms.pkg.pki.CertResolver; import hk.hku.cecid.ebms.pkg.pki.CompositeKeyStore; import hk.hku.cecid.ebms.pkg.pki.ErrorMessages; import hk.hku.cecid.ebms.pkg.pki.SignException; import hk.hku.cecid.ebms.pkg.pki.VerifyException; import hk.hku.cecid.ebms.pkg.validation.SOAPValidationException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.Iterator; import javax.activation.DataSource; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPPart; import javax.xml.transform.TransformerFactory; import org.apache.log4j.Logger; import org.dom4j.io.DocumentResult; import org.dom4j.io.XMLWriter; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; /** * An implementation of ebXML <code>Signature</code>, making use * of Phoenix's PKI library. * * @author kcyee * @version $Revision: 1.1 $ */ class PKISignatureImpl extends Signature { static Logger logger = Logger.getLogger(PKISignatureImpl.class); private final EbxmlMessage ebxmlMessage; PKISignatureImpl(EbxmlMessage ebxmlMessage) throws SOAPException { super(ebxmlMessage.getSOAPMessage().getSOAPPart().getEnvelope(), ELEMENT_SIGNATURE, NAMESPACE_PREFIX_DS, NAMESPACE_URI_DS); this.ebxmlMessage = ebxmlMessage; } PKISignatureImpl(EbxmlMessage ebxmlMessage, SOAPEnvelope soapEnvelope, SOAPElement soapElement) throws SOAPException { super(soapEnvelope, soapElement); this.ebxmlMessage = ebxmlMessage; } void addReference(String uri) { throw new Error("Not supported"); } void sign(String alias, char[] password, String keyStoreLocation) throws SignatureException { sign(alias, password, keyStoreLocation, null); } void sign(String alias, char[] password, String keyStoreLocation, String algo) throws SignatureException { sign(alias, password, keyStoreLocation, algo, null, false); } private String getAlgorithmFromPrivateKey(PrivateKey key) throws SignException { String keyAlgo = key.getAlgorithm().toLowerCase(); if (keyAlgo.equals("dsa")) { return "dsa-sha1"; } else if (keyAlgo.equals("rsa")) { return "rsa-sha1"; } else { throw new SignException("Unknown key algorithm : " + keyAlgo); } } private boolean isAlgorithmMatchedWithKey(PrivateKey key, String algo) { String keyAlgo = key.getAlgorithm().toLowerCase(); return algo.startsWith(keyAlgo); } void sign(String alias, char[] password, String keyStoreLocation, String algo, String digestAlgo, boolean signEnvelopeOnly) throws SignatureException { try { final SOAPPart soapPart = ebxmlMessage.getSOAPMessage(). getSOAPPart(); DocumentResult docResult = new DocumentResult(); TransformerFactory.newInstance().newTransformer(). transform(soapPart.getContent(), docResult); ByteArrayOutputStream baos = new ByteArrayOutputStream(); (new XMLWriter(baos)).write(docResult.getDocument()); DocumentBuilderFactory factory = DocumentBuilderFactory. newInstance(); factory.setNamespaceAware(true); // soapPartDocument is a DOM equivilance of soapPart final Document soapPartDocument = factory.newDocumentBuilder(). parse(new ByteArrayInputStream(baos.toByteArray())); final String soapHeaderName = soapPart.getEnvelope().getHeader(). getElementName().getLocalName(); final Element soapHeader = (Element) soapPartDocument. getElementsByTagNameNS(NAMESPACE_URI_SOAP_ENVELOPE, soapHeaderName).item(0); ApacheXMLDSigner signature = new ApacheXMLDSigner(); CompositeKeyStore ks = new CompositeKeyStore(); ks.addKeyStoreFile(keyStoreLocation, null, password); PrivateKey pk = (PrivateKey) ks.getKey(alias, password); /* * To reduce the complexity of the exception message, * the exception can be catch on the final catch block. * try { pk = (PrivateKey) ks.getKey(alias, password); if(pk ==null){ String message ="Cannot retrieve key from keystore["+keyStoreLocation+"]" +"\n"+ "KeyStore Type: " + ks.getKeyStore().getType() +"\n"+ "Key Provider: " + ks.getKeyStore().getProvider().getInfo(); throw new NullPointerException(message); } } catch (Exception e) { String err = "Cannot get private key: " + alias + " - " + e.getMessage(); logger.warn(err); throw new SignException(err); }*/ if (algo != null) { if (!isAlgorithmMatchedWithKey(pk, algo)) { throw new SignException( "Signing algorithm not matched with key algorithm, " + "actual key algorithum:" + pk.getAlgorithm() +"\t" + "expect algorithum: " + algo ); } if (digestAlgo == null) { signature.setEnvelope(soapPartDocument, algo); } else { signature.setEnvelope(soapPartDocument, algo, digestAlgo); } } else { String keyAlgo = getAlgorithmFromPrivateKey(pk); signature.setEnvelope(soapPartDocument, keyAlgo); } /* if (algo == null) { // use default algorithm, i.e. dsa-sha1 signature.setEnvelope(soapPartDocument); } else { // use user-defined algorithm, only support dsa-sha1 and // rsa-sha1 if (digestAlgo == null) { signature.setEnvelope(soapPartDocument, algo); } else { signature.setEnvelope(soapPartDocument, algo, digestAlgo); } } */ // important, if not, the transformation will fail soapHeader.appendChild(signature.getElement()); if (!signEnvelopeOnly) { Iterator i = ebxmlMessage.getPayloadContainers(); while (i.hasNext()) { PayloadContainer pc = (PayloadContainer) i.next(); signature.addDocument(pc.getHref(), pc.getDataHandler().getInputStream(), pc.getContentType()); } } signature.sign(ks, alias, password); domToSoap(signature.getElement(), this); Iterator childElements = getChildElements (SignatureReference.SIGNATURE_REFERENCE); if (childElements.hasNext()) { while (childElements.hasNext()) { references.add(new SignatureReference(soapEnvelope, (SOAPElement) childElements.next())); } } else { throw new SOAPValidationException(SOAPValidationException. SOAP_FAULT_CLIENT, "<" + NAMESPACE_PREFIX_DS + ":" + SignatureReference.SIGNATURE_REFERENCE + "> is not found in <" + NAMESPACE_PREFIX_DS + ":" + ELEMENT_SIGNATURE + ">!"); } childElements = getChildElements(ELEMENT_SIGNATURE_VALUE); if (childElements.hasNext()) { signatureValue = ((SOAPElement) childElements.next()). getValue(); } else { throw new SOAPValidationException(SOAPValidationException. SOAP_FAULT_CLIENT, "<" + NAMESPACE_PREFIX_DS + ":" + ELEMENT_SIGNATURE_VALUE + "> is not found in <" + NAMESPACE_PREFIX_DS + ":" + ELEMENT_SIGNATURE + ">!"); } } catch (Exception e) { String err = ErrorMessages.getMessage (ErrorMessages.ERR_PKI_CANNOT_SIGN, e); err += "\n"+"Try to retreive key alias["+ alias+"] from keystore["+keyStoreLocation+"]"; throw new SignatureException(err, e); } } void sign(String alias, char[] password) throws SignatureException { throw new Error("Not supported"); } void sign(String alias, char[] password, String keyStoreLocation, String type, String provider) throws SignatureException { throw new Error("Not supported"); } void sign(PrivateKey privateKey, X509Certificate[] certificates) throws SignatureException { throw new Error("Not supported"); } private int loadInputStreamToOutputStream(InputStream istream, OutputStream ostream, int skipped, int expectedLength) throws IOException { int bufferSize = 2048; byte[] buffer = new byte[bufferSize]; int skippedBytes = 0; int read = 0; while(read != -1 && skippedBytes < skipped) { read = istream.read(buffer, 0, calculateReadSize(skippedBytes, skipped, bufferSize)); if (read != -1) { skippedBytes += read; } } int length = 0; read = 0; while(read != -1 && length < expectedLength) { read = istream.read(buffer, 0, calculateReadSize(length, expectedLength, bufferSize)); if (read != -1) { ostream.write(buffer, 0, read); length += read; } } return length; } private int calculateReadSize(int read, int expected, int bufferSize) { int remain = expected - read; if (remain > bufferSize) { return bufferSize; } else { return remain; } } boolean verify(char[] password, String keyStoreLocation, CertResolver certResolver, DataSource dataSource) throws SignatureException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); String fileName = ebxmlMessage.getFileName(); String persistenceName = ebxmlMessage.getPersistenceName(); byte[] soapEnvelopeBytes = ebxmlMessage.getSoapEnvelopeBytes(); if (soapEnvelopeBytes != null) { baos.write(soapEnvelopeBytes); } else if (persistenceName != null) { if (dataSource == null) { throw new SignatureException( "Inconsistence persistence data for : " + persistenceName); } InputStream istream = dataSource.getInputStream(); try { soapEnvelopeBytes = EbxmlMessage.getSoapEnvelopeBytesFromStream(istream); } catch (SOAPException e) { throw e; } finally { istream.close(); } } else if (fileName != null) { InputStream istream = null; try { istream = new FileInputStream(fileName); soapEnvelopeBytes = EbxmlMessage.getSoapEnvelopeBytesFromStream(istream); } catch (IOException e) { throw e; } catch (SOAPException e) { throw e; } finally { if (istream != null) { istream.close(); } } } else { final SOAPPart soapPart = ebxmlMessage.getSOAPMessage(). getSOAPPart(); DocumentResult docResult = new DocumentResult(); TransformerFactory.newInstance().newTransformer(). transform(soapPart.getContent(), docResult); (new XMLWriter(baos)).write(docResult.getDocument()); } DocumentBuilderFactory factory = DocumentBuilderFactory. newInstance(); factory.setNamespaceAware(true); // soapPartDocument is a DOM equivilance of soapPart final Document soapPartDocument = factory.newDocumentBuilder(). parse(new ByteArrayInputStream(baos.toByteArray())); ApacheXMLDSigner signature = new ApacheXMLDSigner(); signature.setEnvelope(soapPartDocument); for (Iterator i=ebxmlMessage.getPayloadContainers(); i.hasNext();) { PayloadContainer pc = (PayloadContainer) i.next(); signature.addDocument(pc.getHref(), pc.getDataHandler(). getInputStream(), pc.getContentType()); } if (keyStoreLocation != null) { CompositeKeyStore ks = new CompositeKeyStore(); ks.addKeyStoreFile(keyStoreLocation, null, password); signature.setTrustAnchor(ks); } signature.addCertResolver(certResolver, ebxmlMessage); return signature.verify(); } catch (VerifyException e) { throw new SignatureException(e.getMessage(), e); } catch (Exception e) { String err = ErrorMessages.getMessage( ErrorMessages.ERR_PKI_UNKNOWN_ERROR, e); throw new SignatureException(err , e); } } boolean verify(Element documentElement, PublicKey key) throws SignatureException { throw new Error("Not supported"); } boolean verify(PublicKey key) throws SignatureException { throw new Error("Not supported"); } private void domToSoap(Element domElement, ExtensionElement element) throws SOAPException { final String parentPrefix = element.getSOAPElement().getElementName(). getPrefix(); final String parentNamespaceUri = element.getSOAPElement(). getElementName().getURI(); final String nsPrefix = XML_NS_DECL_PREFIX + XML_NS_SEPARATOR; final NodeList nodeList = domElement.getChildNodes(); for (int i=0 ; i<nodeList.getLength() ; i++) { final org.w3c.dom.Node node = nodeList.item(i); if (node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) { element.getSOAPElement().addTextNode(node.getNodeValue()); } if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { continue; } final Element child = (Element) node; final ExtensionElement addedChild; if (child.getNamespaceURI().equals(parentNamespaceUri)) { addedChild = element.addChildElement(child.getLocalName()); } else { addedChild = null; } final NamedNodeMap nodeMap = child.getAttributes(); for (int j=0 ; j<nodeMap.getLength() ; j++) { final Attr attribute = (Attr) nodeMap.item(j); final String name = attribute.getName(); final String value = attribute.getValue(); if (!name.equals(nsPrefix + NAMESPACE_PREFIX_DS) && !name.startsWith(XML_NS_DECL_PREFIX)) { addedChild.addAttribute(soapEnvelope. createName(name), value); } } domToSoap(child, addedChild); } } }