Package org.ejbca.core.protocol.xkms.client

Source Code of org.ejbca.core.protocol.xkms.client.XKMSInvoker

/*************************************************************************
*                                                                       *
*  EJBCA: The OpenSource Certificate Authority                          *
*                                                                       *
*  This software is free software; you can redistribute it and/or       *
*  modify it under the terms of the GNU Lesser General Public           *
*  License as published by the Free Software Foundation; either         *
*  version 2.1 of the License, or any later version.                    *
*                                                                       *
*  See terms of license at gnu.org.                                     *
*                                                                       *
*************************************************************************/

package org.ejbca.core.protocol.xkms.client;

import gnu.inet.encoding.StringprepException;

import java.net.MalformedURLException;
import java.net.URL;
import java.security.Key;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;

import org.apache.log4j.Logger;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.transforms.TransformationException;
import org.ejbca.core.protocol.xkms.XKMSService;
import org.ejbca.core.protocol.xkms.common.XKMSConstants;
import org.ejbca.core.protocol.xkms.common.XKMSUtil;
import org.ejbca.util.CertTools;
import org.ejbca.util.CryptoProviderTools;
import org.w3._2002._03.xkms_.LocateRequestType;
import org.w3._2002._03.xkms_.LocateResultType;
import org.w3._2002._03.xkms_.ObjectFactory;
import org.w3._2002._03.xkms_.RecoverRequestType;
import org.w3._2002._03.xkms_.RecoverResultType;
import org.w3._2002._03.xkms_.RegisterRequestType;
import org.w3._2002._03.xkms_.RegisterResultType;
import org.w3._2002._03.xkms_.ReissueRequestType;
import org.w3._2002._03.xkms_.ReissueResultType;
import org.w3._2002._03.xkms_.RequestAbstractType;
import org.w3._2002._03.xkms_.RevokeRequestType;
import org.w3._2002._03.xkms_.RevokeResultType;
import org.w3._2002._03.xkms_.ValidateRequestType;
import org.w3._2002._03.xkms_.ValidateResultType;
import org.w3c.dom.Document;

/**
* Helper class that performs the prefix replacements
* and does the dispatch invokation.
*
*
* @author Philip Vendil 2006 dec 19
*
* @version $Id: XKMSInvoker.java 11009 2010-12-29 15:20:37Z jeklund $
*/

public class XKMSInvoker {

  private static Logger log = Logger.getLogger(XKMSInvoker.class);

  private static JAXBContext jAXBContext = null;
  private static Marshaller marshaller = null;
  private static Unmarshaller unmarshaller = null;
  private static DocumentBuilderFactory dbf = null;
 
  private Collection<Certificate> cacerts = null;

  private static Dispatch<Source> sourceDispatch = null;
  private ObjectFactory xKMSObjectFactory = new ObjectFactory()

  static{     
    try {
      CryptoProviderTools.installBCProvider();
      org.apache.xml.security.Init.init();

      jAXBContext = JAXBContext.newInstance("org.w3._2002._03.xkms_:org.w3._2001._04.xmlenc_:org.w3._2000._09.xmldsig_");       
      marshaller = XKMSUtil.getNamespacePrefixMappedMarshaller(jAXBContext);
      dbf = DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware(true);
      dbf.setExpandEntityReferences(false);
      unmarshaller = jAXBContext.createUnmarshaller();

    } catch (JAXBException e) {
      log.error("Error initializing RequestAbstractTypeResponseGenerator",e);
    }

  }
     
  /**
   * Creates an invoker to the web service at the specified URL
   *
   * @param serviceURL the url to the web service.
   * @param cacerts a collection of trusted CA signing responses. Use null if signed responses are not required.
   */
  public XKMSInvoker(String serviceURL, Collection<Certificate> cacerts){
    XKMSService xkmsService;
    try {
      xkmsService = new XKMSService(new URL(serviceURL + "?wsdl"),new QName("http://www.w3.org/2002/03/xkms#wsdl", "XKMSService"));
      sourceDispatch = xkmsService.createDispatch(new QName("http://www.w3.org/2002/03/xkms#wsdl", "XKMSPort"), Source.class, Service.Mode.PAYLOAD);
    } catch (MalformedURLException e) {
      log.error("Error creating XKMS Service instance",e);
    }      
    this.cacerts = cacerts; // null if signed responses are not required.
  }

  /**
   * Creates a locate call to the web service
   *
   * @param locateRequestType the request
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @return a LocateResultType
   * @throws XKMSResponseSignatureException if the response signature didn't verify
   */
  public LocateResultType locate(LocateRequestType locateRequestType, X509Certificate signCert, Key privateKey) throws XKMSResponseSignatureException{
    JAXBElement<LocateRequestType> locateRequest = xKMSObjectFactory.createLocateRequest(locateRequestType);
    DOMSource domSource = performSigning(locateRequest, locateRequestType.getId(), signCert, privateKey);
    JAXBElement<LocateResultType> response = invoke(domSource);
       
    return response.getValue();
  }
 
  /**
   * Creates a validate call to the web service
   *
   * @param validateRequestType the request
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @return a ValidateResultType
   * @throws XKMSResponseSignatureException if the response signature didn't verify
   */
  public ValidateResultType validate(ValidateRequestType validateRequestType, X509Certificate signCert, Key privateKey) throws XKMSResponseSignatureException{       
    JAXBElement<ValidateRequestType> validateRequest = xKMSObjectFactory.createValidateRequest(validateRequestType);
    DOMSource domSource = performSigning(validateRequest, validateRequestType.getId(), signCert, privateKey);
    JAXBElement<ValidateResultType> response = invoke(domSource);   
   
    return response.getValue();
  }
 
  /**
   * Creates a register call to the web service
   *
   * @param registerRequestType the request
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @param authenticationPassphrase the authenticationkeybinding passphrase, use null if it shouldn't be used.
   * @param pOPPrivateKey private key to sign POP Element, use null to not append POPElement
   * @param prototypeKeyBindingId is of the PrototypeKeyBinding tag.
   * @return a RegisterResultType
   * @throws XKMSResponseSignatureException if the response signature didn't verify
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   */
  public RegisterResultType register(RegisterRequestType registerRequestType, X509Certificate signCert, Key privateKey, String authenticationPassphrase, PrivateKey pOPPrivateKey, String prototypeKeyBindingId) throws XKMSResponseSignatureException, StringprepException{       
    JAXBElement<RegisterRequestType> registerRequest = xKMSObjectFactory.createRegisterRequest(registerRequestType);
    DOMSource domSource = performSigning(registerRequest, registerRequestType.getId(), signCert, privateKey, authenticationPassphrase, pOPPrivateKey, prototypeKeyBindingId);
    JAXBElement<RegisterResultType> response = invoke(domSource);   
   
    return response.getValue();
  }
 
  /**
   * Creates a reissue call to the web service
   *
   * @param reissueRequestType the request
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @param authenticationPassphrase the authenticationkeybinding passphrase, use null if it shouldn't be used.
   * @param pOPPrivateKey private key to sign POP Element, use null to not append POPElement
   * @param reissueKeyBindingId is of the PrototypeKeyBinding tag.
   * @return a ReissueResultType
   * @throws XKMSResponseSignatureException if the response signature didn't verify
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   */
  public ReissueResultType reissue(ReissueRequestType reissueRequestType, X509Certificate signCert, Key privateKey, String authenticationPassphrase, PrivateKey pOPPrivateKey, String reissueKeyBindingId) throws XKMSResponseSignatureException, StringprepException{       
    JAXBElement<ReissueRequestType> reissueRequest = xKMSObjectFactory.createReissueRequest(reissueRequestType);
    DOMSource domSource = performSigning(reissueRequest, reissueRequestType.getId(), signCert, privateKey, authenticationPassphrase, pOPPrivateKey, reissueKeyBindingId);
    JAXBElement<ReissueResultType> response = invoke(domSource);   
   
    return response.getValue();
  }
 
  /**
   * Creates a recover call to the web service
   *
   * @param recoverRequestType the request
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @param authenticationPassphrase the authenticationkeybinding passphrase, use null if it shouldn't be used.
   * @param reissueKeyBindingId is of the PrototypeKeyBinding tag.
   * @return a ReissueResultType
   * @throws XKMSResponseSignatureException if the response signature didn't verify
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   */
  public RecoverResultType recover(RecoverRequestType recoverRequestType, X509Certificate signCert, Key privateKey, String authenticationPassphrase, String recoverKeyBindingId) throws XKMSResponseSignatureException, StringprepException{       
    JAXBElement<RecoverRequestType> recoverRequest = xKMSObjectFactory.createRecoverRequest(recoverRequestType);
    DOMSource domSource = performSigning(recoverRequest, recoverRequestType.getId(), signCert, privateKey, authenticationPassphrase, null, recoverKeyBindingId);
    JAXBElement<RecoverResultType> response = invoke(domSource);   
   
    return response.getValue();
  }
 
  /**
   * Creates a revoke call to the web service
   *
   * @param recvokeRequestType the request
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @param authenticationPassphrase the authenticationkeybinding passphrase, use null if it shouldn't be used.
   * @param revokeKeyBindingId is of the PrototypeKeyBinding tag.
   * @return a RevokeResultType
   * @throws XKMSResponseSignatureException if the response signature didn't verify
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   */
  public RevokeResultType revoke(RevokeRequestType revokeRequestType, X509Certificate signCert, Key privateKey, String authenticationPassphrase, String revokeKeyBindingId) throws XKMSResponseSignatureException, StringprepException{       
    JAXBElement<RevokeRequestType> revokeRequest = xKMSObjectFactory.createRevokeRequest(revokeRequestType);
    DOMSource domSource = performSigning(revokeRequest, revokeRequestType.getId(), signCert, privateKey, authenticationPassphrase, null, revokeKeyBindingId);
    JAXBElement<RevokeResultType> response = invoke(domSource);   
   
    return response.getValue();
  }
 
 
  /**
   * Method that performs the actual invokation.
   * @param abstractMessageType
   * @return
   * @throws XKMSResponseSignatureException
   */
  private JAXBElement invoke(DOMSource domSource) throws XKMSResponseSignatureException{
    JAXBElement result =null;
  
    try{           
      Source response = sourceDispatch.invoke(domSource);
     
      result = (JAXBElement) unmarshaller.unmarshal(response);
      Document x = dbf.newDocumentBuilder().newDocument();
      marshaller.marshal(result, x);
      verifyResponseSignature(x);

      /*DocumentBuilder db = dbf.newDocumentBuilder();
      Document doc = db.parse(((SAXSource) response).getInputSource());
      verifyResponseSignature(doc);
      result = (JAXBElement) unmarshaller.unmarshal(doc);*/
     
     
      //DocumentBuilder db = dbf.newDocumentBuilder();
      //Document doc = db.parse(((StreamSource) response).getInputStream());

      //verifyResponseSignature(doc);
      //result = (JAXBElement) unmarshaller.unmarshal(doc);
    } catch(JAXBException e){
      log.error("Error marshalling XKMS request",e);
    } catch (ParserConfigurationException e) {
      log.error("Error parsing XKMS response",e);
    /*} catch (SAXException e) {
      log.error("Error parsing XKMS response",e);
    } catch (IOException e) {
      log.error("Error parsing XKMS response",e);*/
    }
   
    return result;
  }
 
 
  /**
   * Creates a signature on a request and returns a DOM source.
   *
   * @param messageAbstractType the request to sign
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @return a DOMSource or null if request was invalid
   */
  private DOMSource performSigning(JAXBElement messageAbstractType, String messageId, X509Certificate signCert, Key privateKey){
    DOMSource retval = null;
    try{
      retval = performSigning(messageAbstractType, messageId, signCert, privateKey, null, null, null);
    }catch(StringprepException e){
      // Should never happen
    }
    return retval;
  }
 
  /**
   * Creates a signature on a request and returns a DOM source.
   *
   * @param messageAbstractType the request to sign
   * @param signCert the certificate that should sign the request, or null of no signing should be performed
   * @param privateKey the key doing the signing, or null of no signing should be performed
   * @param authenticationPassphrase the authenticationkeybinding passphrase, use null if it shouldn't be used.
   * @param pOPPrivateKey private key to sign POP Element, use null to not append POPElement
   * @param prototypeKeyBindingId is of the PrototypeKeyBinding tag.
   * @return a DOMSource or null if request was invalid
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   */
  private DOMSource performSigning(JAXBElement messageAbstractType, String messageId, X509Certificate signCert, Key privateKey,
                               String authenticationPassphrase, PrivateKey pOPPrivateKey, String prototypeKeyBindingId) throws StringprepException{
        DOMSource retval = null;
   
      try{
        if(signCert != null && privateKey != null ){
          RequestAbstractType requestAbstractType = (RequestAbstractType) messageAbstractType.getValue();
          requestAbstractType.getResponseMechanism().add(XKMSConstants.RESPONSMEC_REQUESTSIGNATUREVALUE);
        }
       
        Document doc = dbf.newDocumentBuilder().newDocument();
        marshaller.marshal( messageAbstractType, doc );
       
        if(authenticationPassphrase != null){
          doc = XKMSUtil.appendKeyBindingAuthentication(doc, authenticationPassphrase, prototypeKeyBindingId);
        }
       
        if(pOPPrivateKey != null){
          doc = XKMSUtil.appendProofOfPossession(doc, pOPPrivateKey, prototypeKeyBindingId);
        }

        if(signCert != null && privateKey != null ){
          org.apache.xml.security.signature.XMLSignature xmlSig = new org.apache.xml.security.signature.XMLSignature(doc, "", org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1, org.apache.xml.security.c14n.Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
          org.apache.xml.security.transforms.Transforms transforms = new org.apache.xml.security.transforms.Transforms(doc);
          transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
          transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
          xmlSig.addDocument("#" + messageId, transforms, org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);             
          xmlSig.addKeyInfo(signCert);
          doc.getDocumentElement().insertBefore( xmlSig.getElement() ,doc.getDocumentElement().getFirstChild());
          xmlSig.sign(privateKey);       
        }
        retval = new DOMSource(doc);
      }catch(XMLSignatureException e){
        log.error("Error performing XML Signature ",e);
      } catch (TransformationException e) {
        log.error("Error parsing XML request ",e);
      } catch (JAXBException e) {
        log.error("Error parsing XML request ",e);
      } catch (ParserConfigurationException e) {
        log.error("Error parsing XML request ",e);
      } catch (XMLSecurityException e) {
        log.error("Error performing XML Signature ",e);
      }           
    return retval;
  }
 
  /**
   * Method that verifies the response signature,
   *
   * doesn't check the revocation status of the server certificate.
   *
   * @param response, the response from the service
   * @throws {@link XKMSResponseSignatureException} if the signature doesn't verify
   */
  private void verifyResponseSignature(Document doc) throws XKMSResponseSignatureException{
    try{
      /*if (log.isDebugEnabled()) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            XMLUtils.outputDOMc14nWithComments(doc, baos);
            log.debug("verifyResponseSignature XMLUtils.outputDOMc14nWithComments: " + baos.toString());
      }*/
     
      boolean signatureExists = false;

      org.w3c.dom.NodeList xmlSigs = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
      signatureExists = xmlSigs.getLength() > 0;
   
      if(signatureExists && cacerts != null){
        try{                                         
          org.w3c.dom.Element xmlSigElement = (org.w3c.dom.Element)xmlSigs.item(0);

          org.apache.xml.security.signature.XMLSignature xmlVerifySig = new org.apache.xml.security.signature.XMLSignature(xmlSigElement, null);

          org.apache.xml.security.keys.KeyInfo keyInfo = xmlVerifySig.getKeyInfo();
          java.security.cert.X509Certificate verCert = keyInfo.getX509Certificate();

          if (log.isDebugEnabled()) {
            log.debug("verCert SubjectDN:    " + CertTools.getSubjectDN(verCert));
            log.debug("verCert IssuerDN:     " + CertTools.getIssuerDN(verCert));
            log.debug("verCert NotAfter:     " + CertTools.getNotAfter(verCert));
            log.debug("verCert SerialNumber: " + CertTools.getSerialNumberAsString(verCert));

            byte[] signatureValue = xmlVerifySig.getSignatureValue();
            byte[] signatureContent = xmlVerifySig.getSignedInfo().getReferencedContentAfterTransformsItem(0).getBytes();
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
            messageDigest.update(signatureContent);
            byte[] digest = messageDigest.digest();
            log.debug("Signature length:    " + signatureValue.length);
            log.debug("Signature content:    " + new String(signatureContent));
            log.debug("Signature digest len:" + digest.length);
            log.debug("SignatureMethodURI:  " + xmlVerifySig.getSignedInfo().getSignatureMethodURI());
            Signature signer = Signature.getInstance("SHA1withRSA", "BC");
              signer.initVerify(verCert.getPublicKey());
              signer.update(digest);
            log.debug("Signature verifies?: " + signer.verify(signatureValue));
          }
         
          // Check signature
          if(xmlVerifySig.checkSignatureValue(verCert.getPublicKey())){              
           
            Collection<Certificate> cACertChain = cacerts;
            // Check issuer and validity           
            X509Certificate rootCert = null;
            Iterator<Certificate> iter = cACertChain.iterator();
            while(iter.hasNext()){
              X509Certificate cert = (X509Certificate) iter.next();
              if(cert.getIssuerDN().equals(cert.getSubjectDN())){
                rootCert = cert;
                break;
              }
            }

            if(rootCert == null){
              throw new CertPathValidatorException("Error Root CA cert not found in cACertChain");
            }

            List<Certificate> list = new ArrayList<Certificate>();
            list.add(verCert);
            list.addAll(cACertChain);

            CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
            CertStore store = CertStore.getInstance("Collection", ccsp);

            //validating path
            List<Certificate> certchain = new ArrayList<Certificate>();
            certchain.addAll(cACertChain);
            certchain.add(verCert);
            CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);

            Set<TrustAnchor> trust = new HashSet<TrustAnchor>();
            trust.add(new TrustAnchor(rootCert, null));

            CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
            PKIXParameters param = new PKIXParameters(trust);
            param.addCertStore(store);
            param.setDate(new Date());                 
            param.setRevocationEnabled(false);

            cpv.validate(cp, param);
          }else{
            throw new XKMSResponseSignatureException("Error XKMS request signature doesn't verify.");           
          }
        }catch(Exception e){         
          throw new XKMSResponseSignatureException("Error when verifying signature request.",e);
        }
      }else{
        if(cacerts != null){
          throw new XKMSResponseSignatureException("Error XKMS response didn't return and signed response");
        }
      }
        } catch (TransformerFactoryConfigurationError e) {
      log.error("Error when DOM parsing request.",e);
    }
  }
}
TOP

Related Classes of org.ejbca.core.protocol.xkms.client.XKMSInvoker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.