All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.certificateservices.messages.sensitivekeys.SensitiveKeysParser Maven / Gradle / Ivy

The newest version!
/************************************************************************
*                                                                       *
*  Certificate Service - Messages                                       *
*                                                                       *
*  This software is free software; you can redistribute it and/or       *
*  modify it under the terms of the GNU Lesser General Public License   *
*  License as published by the Free Software Foundation; either         *
*  version 3   of the License, or any later version.                    *
*                                                                       *
*  See terms of license at gnu.org.                                     *
*                                                                       *
*************************************************************************/
package org.certificateservices.messages.sensitivekeys;

import org.certificateservices.messages.MessageContentException;
import org.certificateservices.messages.MessageProcessingException;
import org.certificateservices.messages.MessageSecurityProvider;
import org.certificateservices.messages.NoDecryptionKeyFoundException;
import org.certificateservices.messages.csmessages.DefaultCSMessageParser;
import org.certificateservices.messages.csmessages.XSDLSInput;
import org.certificateservices.messages.sensitivekeys.jaxb.AsymmetricKey;
import org.certificateservices.messages.sensitivekeys.jaxb.EncodedKey;
import org.certificateservices.messages.sensitivekeys.jaxb.KeyData;
import org.certificateservices.messages.sensitivekeys.jaxb.ObjectFactory;
import org.certificateservices.messages.utils.XMLEncrypter;
import org.w3c.dom.Document;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Class to generate and parse Sensitive Keys Structures.
 * 
 * It has also methods to create encrypted binary data defined in
 * the credential managment protocol 2.0 specification.
 * 
 * @author Philip Vendil
 *
 */
public class SensitiveKeysParser {

	public static String NAMESPACE = "http://certificateservices.org/xsd/sensitivekeys";

	public static String DEFAULT_VERSION = "2.0";

	public static final String SENSITIVE_KEYS_XSD_SCHEMA_RESOURCE_LOCATION = "/sensitivekeys_schema2_0.xsd";

	private ObjectFactory of = new ObjectFactory();

	private XMLEncrypter xmlEncrypter;

	private Map keyFactoryMap = new HashMap();

	/**
	 * Constructor for Sensitive Keys Parser, this is the default method to create a parser, it should
	 * not be fetched by the PayloadParserRegistry since this is not a CSMessagePayload.
	 *
	 * @param securityProvider the security provider to use for encrypting and decrypting messages.
	 * @throws MessageProcessingException if internal problems occurred initializing the parser.
	 */
	public SensitiveKeysParser(MessageSecurityProvider securityProvider) throws MessageProcessingException{
		
		try {
			xmlEncrypter = new XMLEncrypter(securityProvider, getDocumentBuilder(), getMarshaller(), getUnmarshaller());
		} catch (Exception e) {
			throw new MessageProcessingException("Error initializing SensitiveKeysParser: " + e.getMessage(),e);
		}
	}

	/**
	 * Method to parse a unencrypted sensitive key data.
	 * 
	 * @param data a serialized sensitive key XML structure.
	 * @return a unmarshalled KeyData.
	 * @throws MessageContentException if xml data was invalid
	 * @throws MessageProcessingException if internal problems occurred unmarshalling the data.
	 */
	public KeyData parse(byte[] data) throws MessageContentException, MessageProcessingException{
		Document doc;
		try {
			doc = getDocumentBuilder().parse(new ByteArrayInputStream(data));
			return (KeyData) getUnmarshaller().unmarshal(doc);
		} catch (SAXException e) {
			throw new MessageContentException("Message content error when parsing sensitive key data: " + e.getMessage(), e);
		} catch (IOException e) {
			throw new MessageContentException("Message content error when parsing sensitive key data: " + e.getMessage(), e);
		}catch (JAXBException e) {
			throw new MessageContentException("Message content error when parsing sensitive key data: " + e.getMessage(), e);
		} catch (ParserConfigurationException e) {
			throw new MessageProcessingException("Internal error when parsing sensitive key data: " + e.getMessage(), e);
		} 
	}
	
	/**
	 * Method to decrypt and unmarshall a sensitive key data.
	 * 

* The encrypted data should be a XML Encryption (http://www.w3.org/2001/04/xmlenc#) EncryptedData element.. * @param encryptedData the encrypted XML data. * @return an unmarshalled HardTokenData * @throws MessageContentException if xml data was invalid. * @throws MessageProcessingException if internal problems occurred unmarshalling or decrypting the data. * @throws NoDecryptionKeyFoundException if decryption key couldn't be found in security provider. */ public KeyData decryptAndParse(byte[] encryptedData) throws MessageContentException, MessageProcessingException, NoDecryptionKeyFoundException{ try { Document doc = getDocumentBuilder().parse(new ByteArrayInputStream(encryptedData)); return (KeyData) xmlEncrypter.decryptDocument(doc); } catch (ParserConfigurationException e) { throw new MessageProcessingException("Internal error parsing encrypted sensitive key data: " + e.getMessage(),e); } catch (SAXException e) { throw new MessageContentException("Internal content encrypted sensitive key data: " + e.getMessage(),e); } catch (IOException e) { throw new MessageContentException("Internal content encrypted sensitive key data: " + e.getMessage(),e); } } /** * Method to convert an asymmetric key pair. * * @param asymmetricKey the java.security.KeyPair to convert, never null- * @return a newly generated key data. */ public KeyData genKeyData(KeyPair asymmetricKey){ return genKeyData(encodeKey(asymmetricKey.getPublic()),encodeKey(asymmetricKey.getPrivate())); } /** * Method to create a key data for a asymmetric key. * * @param publicKey the public key of key pair to use, not null. * @param privateKey the private key of key pair to use, not null. * @return a newly generated key data. */ public KeyData genKeyData(EncodedKey publicKey, EncodedKey privateKey) { KeyData kd = of.createKeyData(); kd.setVersion(DEFAULT_VERSION); AsymmetricKey ak = of.createAsymmetricKey(); ak.setPublicKey(publicKey); ak.setPrivateKey(privateKey); kd.setAsymmetricKey(ak); return kd; } /** * Method to create a key data for a symmetric java.security.Key. * * @param symmetricKey the symmetric key to use, not null. * @return a newly generated key data. * @throws MessageProcessingException if date convertion fails. */ public KeyData genKeyData(Key symmetricKey) { return genKeyData(encodeKey(symmetricKey)); } /** * Method to create a key data for a symmetric key. * * @param symmetricKey the symmetric key to use, not null. * @return a newly generated key data. * @throws MessageProcessingException if date convertion fails. */ public KeyData genKeyData(EncodedKey symmetricKey){ KeyData kd = of.createKeyData(); kd.setVersion(DEFAULT_VERSION); kd.setSymmetricKey(symmetricKey); return kd; } /** * Method to recreate the java.security symmetric key from a key data. * * @param keyData the key data that must contain a symmetric key element. * @return a secret key. * @throws MessageContentException if key data didn't contain any valid secret key. */ public SecretKey getSymmetricKey(KeyData keyData) throws MessageContentException{ if(keyData.getSymmetricKey() == null){ throw new MessageContentException("Error extracting symmetric key from key data, no symmetric key data found."); } return new SecretKeySpec(keyData.getSymmetricKey().getData(),keyData.getSymmetricKey().getAlgorithm()); } /** * Method to recreate the java.security asymmetric key from a key data. * * @param keyData the key data that must contain a asymmetric key element. * @return a reconstructed key pair.. * @throws MessageContentException if key data didn't contain any valid key pair or had unsupported algorithms. */ public KeyPair getAssymmetricKey(KeyData keyData) throws MessageContentException{ if(keyData.getAsymmetricKey() == null){ throw new MessageContentException("Error extracting asymmetric key from key data, no asymmetric key data found."); } try { PublicKey publicKey = null; EncodedKey pubKeyEncoded = keyData.getAsymmetricKey().getPublicKey(); KeyFactory keyFactory = getKeyFactory(pubKeyEncoded.getAlgorithm()); if(pubKeyEncoded.getFormat().equals("X.509")){ publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(pubKeyEncoded.getData())); } PrivateKey privateKey = null; EncodedKey privateKeyEncoded = keyData.getAsymmetricKey().getPrivateKey(); if(privateKeyEncoded.getFormat().equals("PKCS#8")){ privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyEncoded.getData())); } if(publicKey != null && privateKey != null){ return new KeyPair(publicKey,privateKey); } throw new MessageContentException("Invalid key specification, unsupported encoding format for assymetric key, public key: " + pubKeyEncoded.getFormat() + ", private key: " + privateKeyEncoded.getFormat() ); } catch (InvalidKeySpecException e) { throw new MessageContentException("Invalid key specification in KeyData XML: " + e.getMessage(), e); } } protected EncodedKey encodeKey(Key key){ EncodedKey ek = of.createEncodedKey(); ek.setAlgorithm(key.getAlgorithm()); ek.setFormat(key.getFormat()); ek.setData(key.getEncoded()); return ek; } /** * Method to serialize a sensitive key data to a byte array. * * @param key the sensitive key data to serialize * @return a serialized version of the sensitive key data. * @throws MessageContentException if hard token data contained invalid content. * @throws MessageProcessingException if internal problems occurred marshalling the data. */ public byte[] marshall(KeyData key) throws MessageContentException, MessageProcessingException{ Document doc; try { doc = getDocumentBuilder().newDocument(); getMarshaller().marshal(key, doc); StringWriter writer = new StringWriter(); getTransformer().transform(new DOMSource(doc), new StreamResult(writer)); String output = writer.getBuffer().toString(); return output.getBytes("UTF-8"); } catch (JAXBException e) { throw new MessageContentException("Message content error when generating sensitive key data: " + e.getMessage(), e); } catch (TransformerException e) { throw new MessageProcessingException("Internal error when generating sensitive key data: " + e.getMessage(), e); } catch (UnsupportedEncodingException e) { throw new MessageProcessingException("Internal error when generating sensitive key data: " + e.getMessage(), e); }catch (ParserConfigurationException e) { throw new MessageProcessingException("Internal error when generating sensitive key data: " + e.getMessage(), e); } } /** * Method to serialize and encrypt a sensitive key data to a given list of recipients. *

* The data will first be transformed into a encrypted data. * * @param key the sensitive key data to serialize * @param recipients a list of recipients to encrypt the data to. * @return a serialized version of the hard token data. * @throws MessageContentException if hard token data contained invalid content. * @throws MessageProcessingException if internal problems occurred marshalling the data. */ public byte[] encryptAndMarshall(KeyData key, List recipients) throws MessageContentException, MessageProcessingException{ Document doc; try { doc = getDocumentBuilder().newDocument(); getMarshaller().marshal(key, doc); Document encDoc = xmlEncrypter.encryptElement(doc, recipients, false); StringWriter writer = new StringWriter(); getTransformer().transform(new DOMSource(encDoc), new StreamResult(writer)); String output = writer.getBuffer().toString(); return output.getBytes("UTF-8"); } catch (JAXBException e) { throw new MessageContentException("Message content error when generating sensitive key data: " + e.getMessage(), e); } catch (TransformerException e) { throw new MessageProcessingException("Internal error when generating sensitive key data: " + e.getMessage(), e); } catch (UnsupportedEncodingException e) { throw new MessageProcessingException("Internal error when generating sensitive key data: " + e.getMessage(), e); }catch (ParserConfigurationException e) { throw new MessageProcessingException("Internal error when generating sensitive key data: " + e.getMessage(), e); } } private DocumentBuilder getDocumentBuilder() throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); return dbf.newDocumentBuilder(); } Marshaller getMarshaller() throws JAXBException{ Marshaller marshaller = getJAXBContext().createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); return marshaller; } Unmarshaller getUnmarshaller() throws JAXBException, SAXException{ Unmarshaller unmarshaller = getJAXBContext().createUnmarshaller(); unmarshaller.setSchema(getSchema()); return unmarshaller; } private JAXBContext jaxbContext = null; /** * Help method maintaining the Assertion JAXB Context. */ private JAXBContext getJAXBContext() throws JAXBException{ if(jaxbContext== null){ String jaxbClassPath = "org.certificateservices.messages.sensitivekeys.jaxb:org.certificateservices.messages.xenc.jaxb:org.certificateservices.messages.xmldsig.jaxb"; jaxbContext = JAXBContext.newInstance(jaxbClassPath); } return jaxbContext; } private Schema schema = null; private Schema getSchema() throws SAXException { if(schema == null){ schema = generateSchema(); } return schema; } private Schema generateSchema() throws SAXException{ SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); schemaFactory.setResourceResolver(new SensitiveKeysParserLSResourceResolver()); Source[] sources = new Source[3]; sources[0] = new StreamSource(getClass().getResourceAsStream(DefaultCSMessageParser.XMLDSIG_XSD_SCHEMA_RESOURCE_LOCATION)); sources[1] = new StreamSource(getClass().getResourceAsStream(DefaultCSMessageParser.XMLENC_XSD_SCHEMA_RESOURCE_LOCATION)); sources[2] = new StreamSource(getClass().getResourceAsStream(SENSITIVE_KEYS_XSD_SCHEMA_RESOURCE_LOCATION)); Schema schema = schemaFactory.newSchema(sources); return schema; } private Transformer transformer = null; private Transformer getTransformer() throws MessageProcessingException{ if(transformer == null){ try { TransformerFactory tf = TransformerFactory.newInstance(); transformer = tf.newTransformer(); } catch (TransformerConfigurationException e) { throw new MessageProcessingException("Error instanciating Transformer for XMLSigner: " + e.getMessage(),e); } } return transformer; } public class SensitiveKeysParserLSResourceResolver implements LSResourceResolver { public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { try { if(systemId != null && systemId.equals("datatypes.dtd")){ return new XSDLSInput(publicId, systemId, DefaultCSMessageParser.class.getResourceAsStream("/datatypes.dtd")); } if(systemId != null && systemId.equals("http://www.w3.org/2001/XMLSchema.dtd")){ return new XSDLSInput(publicId, systemId, DefaultCSMessageParser.class.getResourceAsStream("/XMLSchema.dtd")); } if(namespaceURI != null){ if(namespaceURI.equals(DefaultCSMessageParser.XMLENC_NAMESPACE)){ return new XSDLSInput(publicId, systemId, DefaultCSMessageParser.class.getResourceAsStream(DefaultCSMessageParser.XMLENC_XSD_SCHEMA_RESOURCE_LOCATION)); } if(namespaceURI.equals(DefaultCSMessageParser.XMLDSIG_NAMESPACE)){ return new XSDLSInput(publicId, systemId, DefaultCSMessageParser.class.getResourceAsStream(DefaultCSMessageParser.XMLDSIG_XSD_SCHEMA_RESOURCE_LOCATION)); } if(namespaceURI.equals(NAMESPACE)){ return new XSDLSInput(publicId, systemId, DefaultCSMessageParser.class.getResourceAsStream(SENSITIVE_KEYS_XSD_SCHEMA_RESOURCE_LOCATION)); } } } catch (MessageProcessingException e) { throw new IllegalStateException("Error couldn't read XSD from class path: " + e.getMessage(), e); } return null; } } private KeyFactory getKeyFactory(String algoritm) throws MessageContentException { KeyFactory retval = keyFactoryMap.get(algoritm); if(retval == null){ try { retval = KeyFactory.getInstance(algoritm); } catch (NoSuchAlgorithmException e) { throw new MessageContentException("Error in KeyData XML, unsupported key algorithm: " + algoritm); } keyFactoryMap.put(algoritm,retval); } return retval; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy