Package org.ejbca.core.protocol.xkms.common

Source Code of org.ejbca.core.protocol.xkms.common.XKMSUtil

/*************************************************************************
*                                                                       *
*  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.common;

import gnu.inet.encoding.Stringprep;
import gnu.inet.encoding.StringprepException;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.apache.xml.security.algorithms.SignatureAlgorithm;
import org.apache.xml.security.encryption.EncryptedData;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.encryption.XMLEncryptionException;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.EncryptionConstants;
import org.ejbca.util.CryptoProviderTools;
import org.w3._2001._04.xmlenc_.EncryptedDataType;
import org.w3._2002._03.xkms_.ObjectFactory;
import org.w3._2002._03.xkms_.PrivateKeyType;
import org.w3._2002._03.xkms_.RSAKeyPairType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* A util class containing static help methods to process various
* XKMS messages
*
*
* @author Philip Vendil 2006 dec 30
*
* @version $Id: XKMSUtil.java 11212 2011-01-17 09:52:28Z anatom $
*/

public class XKMSUtil {
 
  /** HMAC-SHA1 initial key values */
  public static final byte[] KEY_AUTHENTICATION = {0x1};
  public static final byte[] KEY_REVOCATIONCODEIDENTIFIER_PASS1 = {0x2};
  public static final byte[] KEY_REVOCATIONCODEIDENTIFIER_PASS2 = {0x3};
  public static final byte[] KEY_PRIVATEKEYDATA = {0x4};
 
  private static final String ENCRYPTION_ALGORITHMURI = XMLCipher.TRIPLEDES;
  private static final String SHAREDSECRET_HASH_ALGORITH = "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
 
  private static Logger log = Logger.getLogger(XKMSUtil.class);
 
  private static ObjectFactory xKMSObjectFactory = new ObjectFactory();
 
  private static JAXBContext jAXBContext = null;
  private static Marshaller marshaller = null;
  private static Unmarshaller unmarshaller = null;
  private static DocumentBuilderFactory dbf = null;
 
  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 = getNamespacePrefixMappedMarshaller(jAXBContext);
      dbf = DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware(true);
      unmarshaller = jAXBContext.createUnmarshaller();

    } catch (JAXBException e) {
      log.error("Error initializing RequestAbstractTypeResponseGenerator",e);
    }
  }
 
  /** We use our own NamespacePrefixMapper. There are different implementations of this however, and java switched in
   * jdk 1.6u18, but you can still stumble upon RI implementations which is why we have to do a bit of testing in this method.
   * This depends on the jar jaxb-NamespacePrefixMapper-interfaces-2.0.0.jar when compiling since we need both:
   * com.sun.xml.internal.bind.namespacePrefixMapper and com.sun.xml.bind.namespacePrefixMapper in order to compile.
   *
   * See: http://pragmaticintegration.blogspot.com/2007/11/moving-jaxb-20-applications-built-by.html
   *
   * @param jAXBContext
   * @return Marshaller
   * @throws JAXBException
   */
  public static Marshaller getNamespacePrefixMappedMarshaller(JAXBContext jAXBContext) throws JAXBException {
    Marshaller marshaller = jAXBContext.createMarshaller();
    try {
      try {
        // Use JAXB distributed in Java 6 - note 'internal'
        Object o = Class.forName("org.ejbca.core.protocol.xkms.common.XKMSNamespacePrefixMapper").newInstance();
        marshaller.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", o);       
        if (log.isTraceEnabled()) {
          log.debug("using com.sun.xml.internal.bind.namespacePrefixMapper (JAXB in Java6?)");         
        }
      } catch (PropertyException e) {
        // Reference implementation appears to be present (in endorsed dir?)
        Object o = Class.forName("org.ejbca.core.protocol.xkms.common.XKMSNamespacePrefixMapperRI").newInstance();
        marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", o);
        if (log.isTraceEnabled()) {
          log.debug("using com.sun.xml.bind.namespacePrefixMapper (JAXB RI?)");         
        }
      }
    } catch( PropertyException e ) {
      log.error("Error registering new namespace mapper property.",e);
    } catch (InstantiationException e) {
      log.error("Error instantiating new namespace mapper object.",e);
    } catch (IllegalAccessException e) {
      log.error("Error instantiating new namespace mapper object.",e);
    } catch (ClassNotFoundException e) {
      log.error("Error instantiating new namespace mapper object.",e);
    } catch (NoClassDefFoundError e) {
      log.error("Error instantiating old namespace mapper object.",e);
    }
    return marshaller;
  }

  /**
   * Encrypting a java RSA Private key into a PrivateKeyType object used in register,reissue and recover respolses.
   * using the shared secret.
   *
   * The method uses the HMAC-SHA1 for generating the shared secret
   * and tripple des for encryption
   *
   * @param rSAPrivateKey the privatekey
   * @param sharedSecret the shared secret, cannot be null.
   * @return The Document with the encrypted key included.
   * @throws StringprepException if the shared secret doesn't conform with the SASLprep profile as specified in the XKMS specification.
   * @throws XMLEncryptionException if any other exception occurs during the processing.
   */
  public static PrivateKeyType getEncryptedXMLFromPrivateKey(RSAPrivateCrtKey rSAPrivateKey, String sharedSecret) throws StringprepException, XMLEncryptionException{
    PrivateKeyType privateKeyType = null;
    try{
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document rSAKeyPairDoc = db.newDocument();

        SecretKey sk = getSecretKeyFromPassphrase(sharedSecret,true, 24, KEY_PRIVATEKEYDATA);
       
        RSAKeyPairType rSAKeyPairType = xKMSObjectFactory.createRSAKeyPairType();
      
        rSAKeyPairType.setModulus(rSAPrivateKey.getModulus().toByteArray());
        rSAKeyPairType.setExponent(rSAPrivateKey.getPublicExponent().toByteArray());
        rSAKeyPairType.setP(rSAPrivateKey.getPrimeP().toByteArray());
        rSAKeyPairType.setQ(rSAPrivateKey.getPrimeQ().toByteArray());
        rSAKeyPairType.setDP(rSAPrivateKey.getPrimeExponentP().toByteArray());
        rSAKeyPairType.setDQ(rSAPrivateKey.getPrimeExponentQ().toByteArray());
        rSAKeyPairType.setInverseQ(rSAPrivateKey.getCrtCoefficient().toByteArray());
        rSAKeyPairType.setD(rSAPrivateKey.getPrivateExponent().toByteArray());

        JAXBElement<RSAKeyPairType> rSAKeyPair = xKMSObjectFactory.createRSAKeyPair(rSAKeyPairType);

    marshaller.marshal( rSAKeyPair, rSAKeyPairDoc );

    Document envelopedDoc = db.newDocument();
    Element unencryptedElement = envelopedDoc.createElement("PrivateKey");
    envelopedDoc.appendChild(unencryptedElement);
    Element node = (Element) envelopedDoc.adoptNode(rSAKeyPairDoc.getDocumentElement());
    unencryptedElement.appendChild(node);
   
        Element rootElement = envelopedDoc.getDocumentElement();
      
       
        XMLCipher xmlCipher =
            XMLCipher.getProviderInstance(ENCRYPTION_ALGORITHMURI,"BC");
        xmlCipher.init(XMLCipher.ENCRYPT_MODE, sk);

        EncryptedData encryptedData = xmlCipher.getEncryptedData();
        encryptedData.setMimeType("text/xml");
       
        xmlCipher.doFinal(envelopedDoc,rootElement,true);     

        JAXBElement unmarshalledData = (JAXBElement) unmarshaller.unmarshal(envelopedDoc.getDocumentElement().getFirstChild());
       
        EncryptedDataType encryptedDataType = (EncryptedDataType) unmarshalledData.getValue();
        privateKeyType = xKMSObjectFactory.createPrivateKeyType();
        privateKeyType.setEncryptedData(encryptedDataType);
       
    } catch (ParserConfigurationException e) {
      log.error("Error encryption private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (XMLSecurityException e) {
      log.error("Error encryption private key", e);
      throw new XMLEncryptionException(e.getMessage(),e)
    } catch (JAXBException e) {
      log.error("Error encryption private key", e);
      throw new XMLEncryptionException(e.getMessage(),e)
    } catch (Exception e) {
      log.error("Error encryption private key", e);
      throw new XMLEncryptionException(e.getMessage(),e)
    }          
   
    return privateKeyType;
  }
 
  /**
   * Method to get the private key from an XKMS message with an encrypted
   * PrivateKey tag. The method uses the HMAC-SHA1 for generating the shared secret
   * and tripple des for encryption.
   *
   * @param privateKeyType the JAXB version of the PrivateKey tag
   * @param sharedSecret the shared secret, cannot be null.
   * @return a java RSAPrivateKey
   * @throws StringprepException if the shared secret doesn't conform with the SASLprep profile as specified in the XKMS specification.
   * @throws XMLEncryptionException if any other exception occurs during the processing.
   */
  public static RSAPrivateKey getPrivateKeyFromEncryptedXML(PrivateKeyType privateKeyType, String sharedSecret) throws StringprepException, XMLEncryptionException{
    RSAPrivateKey privkey2 = null;
    try{
    DocumentBuilder db = dbf.newDocumentBuilder();
        Document privateKeyDoc = db.newDocument();
        marshaller.marshal(privateKeyType, privateKeyDoc);
       
        Element encryptedDataElement =
            (Element) privateKeyDoc.getElementsByTagNameNS(
                EncryptionConstants.EncryptionSpecNS,
                EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);

        SecretKey sk = getSecretKeyFromPassphrase(sharedSecret,true, 24, KEY_PRIVATEKEYDATA);
                       
        XMLCipher xmlDecipher = XMLCipher.getProviderInstance(ENCRYPTION_ALGORITHMURI,"BC");
       
        xmlDecipher.init(XMLCipher.DECRYPT_MODE, sk);
      
        xmlDecipher.doFinal(privateKeyDoc, encryptedDataElement);
       
        JAXBElement<RSAKeyPairType> rSAKeyPair = (JAXBElement<RSAKeyPairType>) unmarshaller.unmarshal(privateKeyDoc.getDocumentElement().getFirstChild());
       
        RSAKeyPairType rSAKeyPairType = rSAKeyPair.getValue();
       
               
        RSAPrivateCrtKeySpec rSAPrivateKeySpec = new RSAPrivateCrtKeySpec(new BigInteger(rSAKeyPairType.getModulus()), new BigInteger(rSAKeyPairType.getExponent()),
                         new BigInteger(rSAKeyPairType.getD()), new BigInteger(rSAKeyPairType.getP()),
                         new BigInteger(rSAKeyPairType.getQ()), new BigInteger(rSAKeyPairType.getDP()),
                         new BigInteger(rSAKeyPairType.getDQ()), new BigInteger(rSAKeyPairType.getInverseQ()));
       
        privkey2 = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(rSAPrivateKeySpec);
       
    } catch (InvalidKeySpecException e) {
      log.error("Error decrypting private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (NoSuchAlgorithmException e) {
      log.error("Error decrypting private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (XMLSecurityException e) {
      log.error("Error decrypting private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (JAXBException e) {
      log.error("Error decrypting private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (ParserConfigurationException e) {
      log.error("Error decrypting private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (Exception e) {
      log.error("Error decrypting private key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    }
   
    return privkey2;
  }
 
  /**
   * Genereates a secret key from a passphrase according to the
   * XKMS specifikation. The HMAC-SHA1 algorithm is used.
   *
   * The passphrase is first checked against SALSPrep profile
   * according to the XKMS specificatiom
   *
   * @param passphrase the passphrase to use, may no be null
   * @param performSASLprep if sASLprep should be called on the input string.
   * @param keylength the length of the key returned.
   * @param keyType one of the initial KEY_ constants
   * @return The SecretKey used in encryption/decryption
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   * @throws XMLEncryptionException If any other exception occured during generation.
   */
  public static SecretKey getSecretKeyFromPassphrase(String passphrase, boolean performSASLprep, int keylength, byte[] keyType) throws  StringprepException, XMLEncryptionException{
    SecretKey retval = null;
    try{
    byte[] finalKey = new byte[keylength];
   
    int keyIndex = 0;   
    byte[] currentKey = keyType;
      

    Document doc = dbf.newDocumentBuilder().newDocument();       
        SignatureAlgorithm sa = new SignatureAlgorithm(doc,
            SHAREDSECRET_HASH_ALGORITH,
                33);
       
        // Make the string saslpreped
        String sASLPrepedPassword = passphrase;
        if(performSASLprep){
          sASLPrepedPassword= Stringprep.saslprep(passphrase);
        }
       
        while(keyIndex < keylength){
          SecretKey sk = new SecretKeySpec(currentKey,
              sa.getJCEAlgorithmString());

          Mac m = Mac.getInstance("HmacSHA1");
          m.init(sk);
          m.update(sASLPrepedPassword.getBytes("ISO8859-1"));
          byte[] mac = m.doFinal();
          for(int i=0;i<mac.length;i++){
               if(keyIndex < keylength){
                 finalKey[keyIndex] = mac[i];
                 keyIndex++;
               }else{
                 break;
               }
          }
          mac[0] = (byte) (mac[0] ^ currentKey[0]);
          currentKey = mac;
         
          retval = new SecretKeySpec(finalKey,
              sa.getJCEAlgorithmString());
        }
    }catch(IllegalMonitorStateException e){
     
    } catch (ParserConfigurationException e) {
      log.error("Error generating secret key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (XMLSecurityException e) {
      log.error("Error generating secret key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (NoSuchAlgorithmException e) {
      log.error("Error generating secret key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (InvalidKeyException e) {
      log.error("Error generating secret key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (IllegalStateException e) {
      log.error("Error generating secret key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    } catch (UnsupportedEncodingException e) {
      log.error("Error generating secret key", e);
      throw new XMLEncryptionException(e.getMessage(),e);
    }
       
        return  retval;
  }
 
  /**
   * Method appending a authorization keybinding element to
   * a requestDoc
   *
   * @param requestDoc
   * @param passphrase
   * @param prototypeKeyBindingId
   * @return the requestDoc with authorization appended
   * @throws StringprepException if the passphrase doesn't fullfull the SASLPrep profile
   * @throws XMLSecurityException If any other exception occured during generation.
   */
  public static Document appendKeyBindingAuthentication(Document requestDoc,String passphrase, String prototypeKeyBindingId) throws StringprepException, XMLSecurityException{
       SecretKey sk = XKMSUtil.getSecretKeyFromPassphrase(passphrase, true, 20, XKMSUtil.KEY_AUTHENTICATION);
   
    org.apache.xml.security.signature.XMLSignature authXMLSig = new org.apache.xml.security.signature.XMLSignature(requestDoc, "", org.apache.xml.security.signature.XMLSignature.ALGO_ID_MAC_HMAC_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(requestDoc);   
    transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
    authXMLSig.addDocument("#" + prototypeKeyBindingId, transforms, org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);             
 
    authXMLSig.sign(sk);
   
    Element authenticationElement = requestDoc.createElementNS("http://www.w3.org/2002/03/xkms#", "Authentication");
    Element keyBindingAuthenticationElement = requestDoc.createElementNS("http://www.w3.org/2002/03/xkms#", "KeyBindingAuthentication");
    keyBindingAuthenticationElement.appendChild(authXMLSig.getElement().cloneNode(true));
    authenticationElement.appendChild(keyBindingAuthenticationElement);
    requestDoc.getDocumentElement().appendChild(authenticationElement);
       
        return requestDoc;
  }

    public static Document appendProofOfPossession(Document requestDoc,PrivateKey privateKey, String prototypeKeyBindingId)throws XMLSecurityException{
    org.apache.xml.security.signature.XMLSignature xmlSig = new org.apache.xml.security.signature.XMLSignature(requestDoc, "", org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1, org.apache.xml.security.c14n.Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
    Transforms transforms = new org.apache.xml.security.transforms.Transforms(requestDoc);   
    transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
   
    xmlSig.addDocument("#" + prototypeKeyBindingId, transforms, org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);             
 
    xmlSig.sign(privateKey);  
   
        Element pOPElement = requestDoc.createElementNS("http://www.w3.org/2002/03/xkms#", "ProofOfPossession");
        pOPElement.appendChild(xmlSig.getElement().cloneNode(true));
        requestDoc.getDocumentElement().appendChild(pOPElement);
       
        return requestDoc;
    }
 
}
TOP

Related Classes of org.ejbca.core.protocol.xkms.common.XKMSUtil

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.