org.certificateservices.custom.c2x.its.crypto.DefaultCryptoManager.java Source code

Java tutorial

Introduction

Here is the source code for org.certificateservices.custom.c2x.its.crypto.DefaultCryptoManager.java

Source

/************************************************************************
*                                                                       *
*  Certificate Service -  Car2Car Core                                  *
*                                                                       *
*  This software is free software; you can redistribute it and/or       *
*  modify it under the terms of the GNU Affero 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.custom.c2x.its.crypto;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher;
import org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.ECIES;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.jce.spec.IESParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;
import org.certificateservices.custom.c2x.its.datastructs.SerializationHelper;
import org.certificateservices.custom.c2x.its.datastructs.StructSerializer;
import org.certificateservices.custom.c2x.its.datastructs.basic.EccPoint;
import org.certificateservices.custom.c2x.its.datastructs.basic.EccPointType;
import org.certificateservices.custom.c2x.its.datastructs.basic.EcdsaSignature;
import org.certificateservices.custom.c2x.its.datastructs.basic.EncryptionParameters;
import org.certificateservices.custom.c2x.its.datastructs.basic.HashedId8;
import org.certificateservices.custom.c2x.its.datastructs.basic.IntX;
import org.certificateservices.custom.c2x.its.datastructs.basic.PublicKeyAlgorithm;
import org.certificateservices.custom.c2x.its.datastructs.basic.Signature;
import org.certificateservices.custom.c2x.its.datastructs.basic.SignerInfo;
import org.certificateservices.custom.c2x.its.datastructs.basic.SignerInfoType;
import org.certificateservices.custom.c2x.its.datastructs.basic.SymmetricAlgorithm;
import org.certificateservices.custom.c2x.its.datastructs.cert.Certificate;
import org.certificateservices.custom.c2x.its.datastructs.cert.SubjectAttribute;
import org.certificateservices.custom.c2x.its.datastructs.cert.SubjectAttributeType;
import org.certificateservices.custom.c2x.its.datastructs.msg.EciesNistP256EncryptedKey;
import org.certificateservices.custom.c2x.its.datastructs.msg.HeaderField;
import org.certificateservices.custom.c2x.its.datastructs.msg.HeaderFieldType;
import org.certificateservices.custom.c2x.its.datastructs.msg.Payload;
import org.certificateservices.custom.c2x.its.datastructs.msg.PayloadType;
import org.certificateservices.custom.c2x.its.datastructs.msg.RecipientInfo;
import org.certificateservices.custom.c2x.its.datastructs.msg.SecuredMessage;
import org.certificateservices.custom.c2x.its.datastructs.msg.TrailerField;
import org.certificateservices.custom.c2x.its.datastructs.msg.TrailerFieldType;

/**
 * Default implementation of ITS CryptoManager with support for public key algorithms: ecdsa_nistp256_with_sha256 and ecies_nistp256
 * 
 * @author Philip Vendil, p.vendil@cgi.com
 *
 */
public class DefaultCryptoManager implements CryptoManager {

    protected ECParameterSpec ecNistP256Spec = ECNamedCurveTable.getParameterSpec("P-256");
    protected KeyPairGenerator ecNistP256Generator;
    protected KeyGenerator aES128Generator;
    protected SecureRandom secureRandom = new SecureRandom();
    protected SecP256R1Curve ecNistP256Curve = new SecP256R1Curve();
    protected KeyFactory keyFact;
    protected MessageDigest sha256Digest;

    protected String provider;

    // dummy signatures used to generate signature sizes 
    protected static HashMap<PublicKeyAlgorithm, Signature> dummySignatures = new HashMap<PublicKeyAlgorithm, Signature>();
    static {
        dummySignatures.put(PublicKeyAlgorithm.ecdsa_nistp256_with_sha256,
                new Signature(PublicKeyAlgorithm.ecdsa_nistp256_with_sha256,
                        new EcdsaSignature(PublicKeyAlgorithm.ecdsa_nistp256_with_sha256,
                                new EccPoint(PublicKeyAlgorithm.ecdsa_nistp256_with_sha256,
                                        EccPointType.x_coordinate_only, new BigInteger("0")),
                                new byte[PublicKeyAlgorithm.ecdsa_nistp256_with_sha256.getFieldSize()])));
    }

    /**
     * Initialized Bouncycastle and it's specific key factories
     */
    @Override
    public void setupAndConnect(CryptoManagerParams params) throws IllegalArgumentException,
            NoSuchAlgorithmException, NoSuchProviderException, IOException, BadCredentialsException {
        if (!(params instanceof DefaultCryptoManagerParams)) {
            throw new IllegalArgumentException(
                    "Invalid type of CryptoManagerParams given, expected DefaultCryptoManagerParams");
        }
        provider = ((DefaultCryptoManagerParams) params).getProvider();

        try {
            if (Security.getProvider("BC") == null) {
                Security.addProvider(new BouncyCastleProvider());
            }
            ecNistP256Generator = KeyPairGenerator.getInstance("ECDSA", provider);
            ecNistP256Generator.initialize(ecNistP256Spec, secureRandom);
            sha256Digest = MessageDigest.getInstance("SHA-256", "BC");
            keyFact = KeyFactory.getInstance("ECDSA", "BC");
            aES128Generator = KeyGenerator.getInstance("AES", "BC");
            aES128Generator.init(128);

        } catch (InvalidAlgorithmParameterException e) {
            throw new NoSuchAlgorithmException("InvalidAlgorithmParameterException: " + e.getMessage(), e);
        }
    }

    /**
     *  @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#encryptSecureMessage(SecuredMessage, PublicKeyAlgorithm, List)
     */
    public SecuredMessage encryptSecureMessage(SecuredMessage secureMessage, PublicKeyAlgorithm encryptionAlg,
            List<Certificate> receipients) throws IllegalArgumentException, GeneralSecurityException, IOException {
        return encryptSecureMessage(secureMessage, encryptionAlg, receipients, PayloadType.encrypted);
    }

    protected SecuredMessage encryptSecureMessage(SecuredMessage secureMessage, PublicKeyAlgorithm encryptionAlg,
            List<Certificate> receipients, PayloadType payloadType)
            throws IllegalArgumentException, GeneralSecurityException, IOException {

        if (receipients == null || receipients.size() == 0) {
            throw new IllegalArgumentException("Error encrypting message list of receipients cannot be empty");
        }

        if (encryptionAlg != PublicKeyAlgorithm.ecies_nistp256) {
            throw new IllegalArgumentException("Error unsupported encryption algorithm: " + encryptionAlg);
        }

        secureMessage = new SecuredMessage(secureMessage.getEncoded()); // Make sure to return a clone of the input message.

        byte[] nounce = new byte[12];
        secureRandom.nextBytes(nounce);
        EncryptionParameters encParams = new EncryptionParameters(encryptionAlg.getRelatedSymmetricAlgorithm(),
                nounce);

        Key symmetricKey = aES128Generator.generateKey();
        List<StructSerializer> reciptientInfos = new ArrayList<StructSerializer>();
        // Verify that all certificates have encryption keys
        for (Certificate c : receipients) {
            org.certificateservices.custom.c2x.its.datastructs.basic.PublicKey encKey = getEncryptionKey(c);
            PublicKey encryptionKey = (PublicKey) decodeEccPoint(encKey.getPublicKeyAlgorithm(),
                    encKey.getPublicKey());
            HashedId8 certHash = new HashedId8(c.getEncoded());
            EciesNistP256EncryptedKey encryptedKey = eCEISEncryptSymmetricKey(encryptionAlg, encryptionKey,
                    symmetricKey);
            reciptientInfos.add(new RecipientInfo(certHash, encryptedKey));
        }

        addHeader(secureMessage, new HeaderField(HeaderFieldType.recipient_info, reciptientInfos));
        addHeader(secureMessage, new HeaderField(encParams));

        for (Payload payload : secureMessage.getPayloadFields()) {
            if (payload.getPayloadType() == payloadType) {
                payload.setData(symmetricEncrypt(encryptionAlg.getRelatedSymmetricAlgorithm(), payload.getData(),
                        symmetricKey, nounce));
            }
        }

        return secureMessage;
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#signAndEncryptSecureMessage(SecuredMessage, Certificate, SignerInfoType, PublicKeyAlgorithm, PrivateKey, PublicKeyAlgorithm, List)
     */
    @Override
    public SecuredMessage encryptAndSignSecureMessage(SecuredMessage secureMessage, Certificate signerCertificate,
            SignerInfoType signInfoType, PublicKeyAlgorithm signAlg, PrivateKey signPrivateKey,
            PublicKeyAlgorithm encryptionAlg, List<Certificate> receipients)
            throws IllegalArgumentException, GeneralSecurityException, IOException {
        SecuredMessage encryptedMessage = encryptSecureMessage(secureMessage, encryptionAlg, receipients,
                PayloadType.signed_and_encrypted);
        return signSecureMessage(encryptedMessage, signerCertificate, signInfoType, signAlg, signPrivateKey);
    }

    /**
     *  @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#decryptSecureMessage(SecuredMessage, Certificate, PrivateKey)
     */
    public SecuredMessage decryptSecureMessage(SecuredMessage secureMessage, Certificate receiverCertificate,
            PrivateKey receiverKey) throws IllegalArgumentException, GeneralSecurityException, IOException {
        return decryptSecureMessage(secureMessage, receiverCertificate, receiverKey, PayloadType.encrypted);
    }

    protected SecuredMessage decryptSecureMessage(SecuredMessage secureMessage, Certificate receiverCertificate,
            PrivateKey receiverKey, PayloadType payloadType)
            throws IllegalArgumentException, GeneralSecurityException, IOException {

        secureMessage = new SecuredMessage(secureMessage.getEncoded()); // Make sure to return a clone of the input message.
        EncryptionParameters encParams = findHeader(secureMessage, HeaderFieldType.encryption_parameters, true)
                .getEncParams();
        RecipientInfo receipentInfo = findRecipientInfo(receiverCertificate,
                findHeader(secureMessage, HeaderFieldType.recipient_info, true).getRecipients());
        Key symmetricKey = eCEISDecryptSymmetricKey(receipentInfo.getPkEncryption(), receiverKey);

        for (Payload payload : secureMessage.getPayloadFields()) {
            if (payload.getPayloadType() == payloadType) {
                payload.setData(
                        symmetricDecrypt(receipentInfo.getPublicKeyAlgorithm().getRelatedSymmetricAlgorithm(),
                                payload.getData(), symmetricKey, encParams.getNounce()));
            }
        }

        return secureMessage;
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#signSecureMessage(SecuredMessage, Certificate, SignerInfoType, PublicKeyAlgorithm, PrivateKey)
     */
    @Override
    public SecuredMessage signSecureMessage(SecuredMessage secureMessage, Certificate signerCertificate,
            SignerInfoType signerInfoType, PublicKeyAlgorithm alg, PrivateKey privateKey)
            throws IllegalArgumentException, SignatureException, IOException {

        if (signerInfoType == SignerInfoType.certificate) {
            addHeader(secureMessage, new HeaderField(new SignerInfo(signerCertificate)));
        } else {
            try {
                HashedId8 hash = new HashedId8(
                        digest(signerCertificate.getEncoded(), PublicKeyAlgorithm.ecdsa_nistp256_with_sha256));
                addHeader(secureMessage, new HeaderField(new SignerInfo(hash)));
            } catch (NoSuchAlgorithmException e) {
                throw new SignatureException(
                        "Error generating secured message, no such algorithm: " + e.getMessage(), e);
            }
        }

        Signature dummySignature = dummySignatures.get(alg);
        byte[] toBeSigned = serializeDataToBeSignedInSecuredMessage(secureMessage, dummySignature);
        Signature signature = signMessage(toBeSigned, alg, privateKey);
        secureMessage.attachSignature(signature);
        return secureMessage;
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#signMessage(byte[], PublicKeyAlgorithm,  PrivateKey)
     */
    @Override
    public Signature signMessage(byte[] message, PublicKeyAlgorithm alg, PrivateKey privateKey)
            throws IllegalArgumentException, SignatureException, IOException {

        ASN1InputStream asn1InputStream = null;
        try {
            byte[] messageDigest = digest(message, alg);

            java.security.Signature signature = java.security.Signature.getInstance("NONEwithECDSA", provider);
            signature.initSign(privateKey);
            signature.update(messageDigest);
            byte[] dERSignature = signature.sign();

            ByteArrayInputStream inStream = new ByteArrayInputStream(dERSignature);
            asn1InputStream = new ASN1InputStream(inStream);

            DLSequence dLSequence = (DLSequence) asn1InputStream.readObject();
            BigInteger r = ((ASN1Integer) dLSequence.getObjectAt(0)).getPositiveValue();
            BigInteger s = ((ASN1Integer) dLSequence.getObjectAt(1)).getPositiveValue();

            ByteArrayOutputStream baos = new ByteArrayOutputStream(alg.getFieldSize());
            SerializationHelper.writeFixedFieldSizeKey(alg, baos, s);

            return new Signature(alg, new EcdsaSignature(alg, new EccPoint(alg, EccPointType.x_coordinate_only, r),
                    baos.toByteArray()));

        } catch (Exception e) {
            if (e instanceof IllegalArgumentException) {
                throw (IllegalArgumentException) e;
            }
            if (e instanceof IOException) {
                throw (IOException) e;
            }
            if (e instanceof SignatureException) {
                throw (SignatureException) e;
            }

            throw new SignatureException(
                    "Internal error generating signature " + e.getClass().getSimpleName() + ": " + e.getMessage(),
                    e);

        } finally {
            if (asn1InputStream != null) {
                asn1InputStream.close();
            }
        }
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifySignature(byte[], Signature, EccPoint)
     */
    @Override
    public boolean verifySignature(byte[] message, Signature signature, EccPoint publicKey)
            throws IllegalArgumentException, SignatureException, IOException {
        if (publicKey.getEccPointType() == EccPointType.x_coordinate_only) {
            throw new IllegalArgumentException(
                    "Error, public key with x_coordinate only isn't supported when verifing signature");
        }

        PublicKey pubKey;
        try {
            pubKey = (PublicKey) decodeEccPoint(signature.getPublicKeyAlgorithm(), publicKey);
        } catch (InvalidKeySpecException e) {
            throw new IllegalArgumentException("Illegal key format when decoding EccPoint key: " + e.getMessage(),
                    e);
        }

        return verifySignature(message, signature, pubKey);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifySignature(byte[], Signature, PublicKey)
     */
    @Override
    public boolean verifySignature(byte[] message, Signature signature, PublicKey publicKey)
            throws IllegalArgumentException, SignatureException, IOException {
        PublicKeyAlgorithm alg = signature.getPublicKeyAlgorithm();

        if (alg == PublicKeyAlgorithm.ecdsa_nistp256_with_sha256) {
            try {
                EcdsaSignature ecdsaSignature = signature.getSignatureValue();

                // Create Signature Data
                ASN1Integer asn1R = new ASN1Integer(ecdsaSignature.getR().getX());
                ASN1Integer asn1S = new ASN1Integer(SerializationHelper.readFixedFieldSizeKey(alg,
                        new ByteArrayInputStream(ecdsaSignature.getSignatureValue())));
                DLSequence dLSequence = new DLSequence(new ASN1Encodable[] { asn1R, asn1S });
                byte[] dERSignature = dLSequence.getEncoded();

                byte[] messageDigest = digest(message, alg);

                java.security.Signature sig = java.security.Signature.getInstance("NONEwithECDSA", provider);
                sig.initVerify(publicKey);
                sig.update(messageDigest);
                return sig.verify(dERSignature);
            } catch (Exception e) {
                if (e instanceof IllegalArgumentException) {
                    throw (IllegalArgumentException) e;
                }
                if (e instanceof IOException) {
                    throw (IOException) e;
                }
                if (e instanceof SignatureException) {
                    throw (SignatureException) e;
                }

                throw new SignatureException("Internal error verifying signature " + e.getClass().getSimpleName()
                        + ": " + e.getMessage(), e);
            }

        } else {
            throw new IllegalArgumentException(
                    "Unsupported signature algoritm: " + signature.getPublicKeyAlgorithm());
        }
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifySignature(byte[], Signature, Certificate)
     */
    @Override
    public boolean verifySignature(byte[] message, Signature signature, Certificate signerCert)
            throws IllegalArgumentException, SignatureException, IOException {
        return verifySignature(message, signature, getVerificationKey(signerCert));
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifyCertificate(Certificate)
     */
    @Override
    public boolean verifyCertificate(Certificate certificate)
            throws IllegalArgumentException, SignatureException, IOException {
        SignerInfo si = findFirstValidSignerInfo(certificate);
        switch (si.getSignerInfoType()) {
        case self:
            return verifyCertificate(certificate, certificate);
        case certificate:
            return verifyCertificate(certificate, si.getCertificate());
        case certificate_chain:
            return verifyCertificate(certificate,
                    si.getCertificateChain().get(si.getCertificateChain().size() - 1));
        default:
            throw new IllegalArgumentException(
                    "No signer info of type self, certificate or certificate_chain found when verifying certificate");
        }
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifyCertificate(Certificate, EccPoint)
     */
    @Override
    public boolean verifyCertificate(Certificate certificate, EccPoint publicKey)
            throws IllegalArgumentException, SignatureException, IOException {
        byte[] certData = serializeCertWithoutSignature(certificate);
        return verifySignature(certData, certificate.getSignature(), publicKey);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifyCertificate(Certificate,  PublicKey)
     */
    @Override
    public boolean verifyCertificate(Certificate certificate, PublicKey publicKey)
            throws IllegalArgumentException, SignatureException, IOException {
        byte[] certData = serializeCertWithoutSignature(certificate);
        return verifySignature(certData, certificate.getSignature(), publicKey);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifyCertificate(Certificate,  Certificate)
     */
    @Override
    public boolean verifyCertificate(Certificate certificate, Certificate signerCert)
            throws IllegalArgumentException, SignatureException, IOException {
        byte[] certData = serializeCertWithoutSignature(certificate);
        return verifySignature(certData, certificate.getSignature(), signerCert);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifySecuredMessage(SecuredMessage)
     */
    @Override
    public void verifySecuredMessage(SecuredMessage message)
            throws IllegalArgumentException, InvalidITSSignatureException, SignatureException, IOException {
        Certificate signerCert = findFirstValidCertificateInMessage(message);
        verifySecuredMessage(message, signerCert);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifySecuredMessage(SecuredMessage, Certificate)
     */
    @Override
    public void verifySecuredMessage(SecuredMessage message, Certificate signerCert)
            throws IllegalArgumentException, InvalidITSSignatureException, SignatureException, IOException {
        Signature signature = findSignatureInMessage(message);
        byte[] msgData = serializeDataToBeSignedInSecuredMessage(message, signature);

        if (!verifySignature(msgData, signature, signerCert)) {
            throw new InvalidITSSignatureException("Error verifying signature of SecuredMessage");
        }
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifyAndDecryptSecuredMessage(SecuredMessage, Certificate, PrivateKey)
     */
    @Override
    public SecuredMessage verifyAndDecryptSecuredMessage(SecuredMessage message, Certificate receiverCertificate,
            PrivateKey receiverKey)
            throws IllegalArgumentException, InvalidITSSignatureException, GeneralSecurityException, IOException {
        verifySecuredMessage(message);
        return decryptSecureMessage(message, receiverCertificate, receiverKey, PayloadType.signed_and_encrypted);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#verifyAndDecryptSecuredMessage(SecuredMessage, Certificate, Certificate, PrivateKey)
     */
    @Override
    public SecuredMessage verifyAndDecryptSecuredMessage(SecuredMessage message, Certificate signerCert,
            Certificate receiverCertificate, PrivateKey receiverKey)
            throws IllegalArgumentException, InvalidITSSignatureException, GeneralSecurityException, IOException {
        verifySecuredMessage(message, signerCert);
        return decryptSecureMessage(message, receiverCertificate, receiverKey, PayloadType.signed_and_encrypted);
    }

    /**
     * Supports ECDSA P256
     * 
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#generateKeyPair(PublicKeyAlgorithm)
     */
    public KeyPair generateKeyPair(PublicKeyAlgorithm alg) {
        if (alg == PublicKeyAlgorithm.ecdsa_nistp256_with_sha256 || alg == PublicKeyAlgorithm.ecies_nistp256) {
            return ecNistP256Generator.generateKeyPair();
        }
        return null;
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#encodeEccPoint(PublicKeyAlgorithm, EccPointType, PublicKey)
     */
    public EccPoint encodeEccPoint(PublicKeyAlgorithm alg, EccPointType type, PublicKey publicKey)
            throws IllegalArgumentException, InvalidKeySpecException {
        if (!(publicKey instanceof java.security.interfaces.ECPublicKey)) {
            throw new IllegalArgumentException(
                    "Only ec public keys are supported, not " + publicKey.getClass().getSimpleName());
        }
        BCECPublicKey bcPub = convertECPublicKeyToBCECPublicKey(alg,
                (java.security.interfaces.ECPublicKey) publicKey);

        if (type == EccPointType.uncompressed) {
            return new EccPoint(alg, type, bcPub.getW().getAffineX(), bcPub.getW().getAffineY());
        }
        if (type == EccPointType.compressed_lsb_y_0 || type == EccPointType.compressed_lsb_y_1) {
            return new EccPoint(alg, bcPub.getQ().getEncoded(true));
        }
        if (type == EccPointType.x_coordinate_only) {
            return new EccPoint(alg, type, bcPub.getW().getAffineX());
        }

        throw new IllegalArgumentException("Unsupported ecc point type: " + type);
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#decodeEccPoint(PublicKeyAlgorithm, EccPoint)
     */
    public Object decodeEccPoint(PublicKeyAlgorithm alg, EccPoint point) throws InvalidKeySpecException {
        switch (point.getEccPointType()) {
        case x_coordinate_only:
            return point.getX();
        case compressed_lsb_y_0:
        case compressed_lsb_y_1:
            return getECPublicKeyFromECPoint(alg, getECCurve(alg).decodePoint(point.getCompressedEncoding()));
        case uncompressed:
            return getECPublicKeyFromECPoint(alg, getECCurve(alg).createPoint(point.getX(), point.getY()));
        }
        return null;
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#digest(byte[], PublicKeyAlgorithm)
     */
    public byte[] digest(byte[] message, PublicKeyAlgorithm alg)
            throws IllegalArgumentException, NoSuchAlgorithmException {
        if (alg == PublicKeyAlgorithm.ecdsa_nistp256_with_sha256) {
            sha256Digest.update(message);
            return sha256Digest.digest();
        } else {
            throw new IllegalArgumentException("Unsupported signature algorithm: " + alg);
        }
    }

    protected final int ECIES_NIST_P256_V_LENGTH = 65;

    /**
     * Help method to perform a ECIES encryption to a recipient of a symmetric key. 
     * 
     * @param publicKeyAlgorithm the algorithm used.
     * @param encryptionKey the public encryption key of the recipient
     * @param symmetricKey the symmetric key to encrypt
     * @return a EciesNistP256EncryptedKey to be included in a SecureMessage header.
     * 
     * @throws InvalidKeyException if supplied key was corrupt.
     * @throws InvalidAlgorithmParameterException if algorithm was badly specified.
     * @throws IllegalBlockSizeException if encrypted data was corrupt.
     * @throws BadPaddingException if encrypted data was corrupt.
     * @throws IllegalArgumentException if arguments where invalid or algorithm not supported.
     * @throws InvalidKeySpecException if supplied key specification was faulty.
     * @throws IOException if communication problem occurred with underlying systems.
     */

    protected EciesNistP256EncryptedKey eCEISEncryptSymmetricKey(PublicKeyAlgorithm publicKeyAlgorithm,
            PublicKey encryptionKey, Key symmetricKey)
            throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException,
            BadPaddingException, IllegalArgumentException, InvalidKeySpecException, IOException {
        if (publicKeyAlgorithm != PublicKeyAlgorithm.ecies_nistp256) {
            throw new IllegalArgumentException(
                    "Unsupported encryption public key algorithm: " + publicKeyAlgorithm);
        }
        byte[] keyData = symmetricKey.getEncoded();

        IESCipher eCIESCipher = new ECIES();
        eCIESCipher.engineInit(Cipher.ENCRYPT_MODE, encryptionKey, new IESParameterSpec(null, null, 128),
                secureRandom);

        byte[] encryptedData = eCIESCipher.engineDoFinal(keyData, 0, keyData.length);
        byte[] v = new byte[ECIES_NIST_P256_V_LENGTH];
        System.arraycopy(encryptedData, 0, v, 0, ECIES_NIST_P256_V_LENGTH);

        EccPoint p = new EccPoint(publicKeyAlgorithm);
        p.deserialize(new DataInputStream(new ByteArrayInputStream(v)));

        byte[] c = new byte[publicKeyAlgorithm.getRelatedSymmetricAlgorithm().getKeyLength()];
        byte[] t = new byte[EciesNistP256EncryptedKey.OUTPUT_TAG_LENGTH];
        System.arraycopy(encryptedData, ECIES_NIST_P256_V_LENGTH, c, 0,
                publicKeyAlgorithm.getRelatedSymmetricAlgorithm().getKeyLength());
        System.arraycopy(encryptedData,
                ECIES_NIST_P256_V_LENGTH + publicKeyAlgorithm.getRelatedSymmetricAlgorithm().getKeyLength(), t, 0,
                EciesNistP256EncryptedKey.OUTPUT_TAG_LENGTH);
        return new EciesNistP256EncryptedKey(publicKeyAlgorithm, p, c, t);
    }

    /**
     * Help method to perform a ECIES decryption of a symmetric key. 
     * 
     * @param eciesNistP256EncryptedKey the EciesNistP256EncryptedKey header value from the SecuredMessage
     * @param decryptionKey the receiptients private key
     * @return a decrypted symmetric key.
     * 
     * @throws InvalidKeyException if supplied key was corrupt.
     * @throws InvalidAlgorithmParameterException if algorithm was badly specified.
     * @throws IllegalBlockSizeException if encrypted data was corrupt.
     * @throws BadPaddingException if encrypted data was corrupt.
     * @throws IllegalArgumentException if arguments where invalid or algorithm not supported.
     * @throws InvalidKeySpecException if supplied key specification was faulty.
     * @throws IOException if communication problem occurred with underlying systems.
     */

    protected Key eCEISDecryptSymmetricKey(EciesNistP256EncryptedKey eciesNistP256EncryptedKey,
            PrivateKey decryptionKey)
            throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException,
            BadPaddingException, IllegalArgumentException, InvalidKeySpecException, IOException {
        if (eciesNistP256EncryptedKey.getPublicKeyAlgorithm() != PublicKeyAlgorithm.ecies_nistp256) {
            throw new IllegalArgumentException("Unsupported encryption public key algorithm: "
                    + eciesNistP256EncryptedKey.getPublicKeyAlgorithm());
        }

        IESCipher eCIESCipher = new ECIES();
        eCIESCipher.engineInit(Cipher.DECRYPT_MODE, decryptionKey, new IESParameterSpec(null, null, 128),
                secureRandom);

        byte[] encryptedData = new byte[ECIES_NIST_P256_V_LENGTH
                + eciesNistP256EncryptedKey.getPublicKeyAlgorithm().getRelatedSymmetricAlgorithm().getKeyLength()
                + EciesNistP256EncryptedKey.OUTPUT_TAG_LENGTH];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dis = new DataOutputStream(baos);

        eciesNistP256EncryptedKey.getV().serialize(dis);
        baos.close();
        System.arraycopy(baos.toByteArray(), 0, encryptedData, 0, ECIES_NIST_P256_V_LENGTH);
        System.arraycopy(eciesNistP256EncryptedKey.getC(), 0, encryptedData, ECIES_NIST_P256_V_LENGTH,
                eciesNistP256EncryptedKey.getPublicKeyAlgorithm().getRelatedSymmetricAlgorithm().getKeyLength());
        System.arraycopy(eciesNistP256EncryptedKey.getT(), 0, encryptedData,
                ECIES_NIST_P256_V_LENGTH + eciesNistP256EncryptedKey.getPublicKeyAlgorithm()
                        .getRelatedSymmetricAlgorithm().getKeyLength(),
                EciesNistP256EncryptedKey.OUTPUT_TAG_LENGTH);

        byte[] decryptedData = eCIESCipher.engineDoFinal(encryptedData, 0, encryptedData.length);

        return new SecretKeySpec(decryptedData, "AES");
    }

    /**
     * Help method to perform a symmetric encrypt of data.
     * 
     * @param symmetricAlgorithm the algorithm used to encrypt the data.
     * @param data the encrypted data.
     * @param symmetricKey the encrypt key.
     * @param nounce related nounce.
     * @return the encrypt clear text data.
     * 
     * @throws IllegalArgumentException if arguments where invalid or algorithm not supported.
     * @throws NoSuchAlgorithmException if algorithm isn't available in underlying provider.
     * @throws IllegalBlockSizeException if encrypted data was corrupt.
     * @throws BadPaddingException if encrypted data was corrupt.
     * @throws InvalidKeyException if supplied key was corrupt.
     * @throws InvalidAlgorithmParameterException if algorithm was badly specified.
     * @throws NoSuchProviderException if underlying provider was't installed properly.
     * @throws NoSuchPaddingException if encrypted data was corrupt.
     */
    protected byte[] symmetricEncrypt(SymmetricAlgorithm symmetricAlgorithm, byte[] data, Key symmetricKey,
            byte[] nounce) throws IllegalArgumentException, NoSuchAlgorithmException, IllegalBlockSizeException,
            BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchProviderException,
            NoSuchPaddingException {

        Cipher c = getSymmetricCipher(symmetricAlgorithm, true);
        c.init(Cipher.ENCRYPT_MODE, symmetricKey, new IvParameterSpec(nounce));

        return c.doFinal(data);
    }

    /**
     * Help method to perform a symmetric decrypt of data.
     * 
     * @param symmetricAlgorithm the algorithm used to encrypt the data.
     * @param data the encrypted data.
     * @param symmetricKey the decryption key.
     * @param nounce related nounce.
     * @return the decrypted clear text data.
     * 
     * @throws IllegalArgumentException if arguments where invalid or algorithm not supported.
     * @throws NoSuchAlgorithmException if algorithm isn't available in underlying provider.
     * @throws IllegalBlockSizeException if encrypted data was corrupt.
     * @throws BadPaddingException if encrypted data was corrupt.
     * @throws InvalidKeyException if supplied key was corrupt.
     * @throws InvalidAlgorithmParameterException if algorithm was badly specified.
     * @throws NoSuchProviderException if underlying provider was't installed properly.
     * @throws NoSuchPaddingException if encrypted data was corrupt.
     */
    protected byte[] symmetricDecrypt(SymmetricAlgorithm symmetricAlgorithm, byte[] data, Key symmetricKey,
            byte[] nounce) throws IllegalArgumentException, NoSuchAlgorithmException, IllegalBlockSizeException,
            BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchProviderException,
            NoSuchPaddingException {

        Cipher c = getSymmetricCipher(symmetricAlgorithm, false);
        c.init(Cipher.DECRYPT_MODE, symmetricKey, new IvParameterSpec(nounce));

        return c.doFinal(data);
    }

    private Map<String, Cipher> chiphers = new HashMap<String, Cipher>();

    protected Cipher getSymmetricCipher(SymmetricAlgorithm symmetricAlgorithm, boolean encrypt)
            throws IllegalArgumentException, NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException {
        Cipher retval = null;
        if (symmetricAlgorithm != SymmetricAlgorithm.aes_128_ccm) {
            throw new IllegalArgumentException("Unsupported symmetric chipher: " + symmetricAlgorithm);
        }
        if (encrypt) {
            retval = chiphers.get(symmetricAlgorithm + "_encrypt");
            if (retval == null) {
                retval = Cipher.getInstance("AES/CCM/NoPadding", "BC");
                chiphers.put(symmetricAlgorithm + "_encrypt", retval);
            }
        } else {
            retval = chiphers.get(symmetricAlgorithm + "_decrypt");
            if (retval == null) {
                retval = Cipher.getInstance("AES/CCM/NoPadding", "BC");
                chiphers.put(symmetricAlgorithm + "_encrypt", retval);
            }
        }

        return retval;
    }

    /**
     * @see org.certificateservices.custom.c2x.its.crypto.CryptoManager#disconnect()
     */
    @Override
    public void disconnect() throws IOException {

    }

    /**
     * Method that adds a header field in a sorted order depending on header field type value.
     * 
     * @param secureMessage secure message to add header value to in sort order.
     * @param headerField header to add.
     */
    protected void addHeader(SecuredMessage secureMessage, HeaderField headerField) {
        List<HeaderField> headerFields = secureMessage.getHeaderFields();
        for (int i = 0; i < headerFields.size(); i++) {
            if (headerFields.get(i).getHeaderFieldType().getByteValue() > headerField.getHeaderFieldType()
                    .getByteValue()) {
                headerFields.add(i, headerField);
                return;
            }
        }

        headerFields.add(headerField);
    }

    /**
     * Help method to find a specific header field in a secure message.
     * 
     * @param secureMessage the secure message to lookup header fields.
     * @param type the type of header to find
     * @param required if exception should be thrown if header could be found.
     * @return header field of specified type, or null if no header field could be found and it's not required.
     * @throws IllegalArgumentException if no required header field of specified type could be found.
     */
    protected HeaderField findHeader(SecuredMessage secureMessage, HeaderFieldType type, boolean required)
            throws IllegalArgumentException {
        HeaderField retval = null;
        for (HeaderField hf : secureMessage.getHeaderFields()) {
            if (hf.getHeaderFieldType() == type) {
                retval = hf;
                break;
            }
        }

        if (required && retval == null) {
            throw new IllegalArgumentException("Couldn't find header of type " + type + " in secure message.");
        }

        return retval;
    }

    /**
     * Help method to find RecipientInfo for a given certificate.
     * 
     * @param receiverCertificate the certificate that matches the RecipientInfo to find.
     * @param recipients list of recipients in message header list.
     * @return the related RecipientInfo
     * @throws IllegalArgumentException if no matching RecipientInfo could be found.
     * @throws IOException if certificate encoding problems occurred. 
     */
    private RecipientInfo findRecipientInfo(Certificate receiverCertificate, List<StructSerializer> recipients)
            throws IllegalArgumentException, IOException {
        HashedId8 hashId = new HashedId8(receiverCertificate.getEncoded());
        for (StructSerializer n : recipients) {
            RecipientInfo ri = (RecipientInfo) n;
            if (ri.getCertId().equals(hashId)) {
                return ri;
            }
        }
        throw new IllegalArgumentException(
                "Error no recipient info found in header matching certificate with hashId: " + hashId.toString());
    }

    protected ECCurve.AbstractFp getECCurve(PublicKeyAlgorithm alg) {
        if (alg == PublicKeyAlgorithm.ecdsa_nistp256_with_sha256 || alg == PublicKeyAlgorithm.ecies_nistp256) {
            return ecNistP256Curve;
        }
        throw new IllegalArgumentException("Unsupported EC Algorithm: " + alg);
    }

    protected ECPublicKey getECPublicKeyFromECPoint(PublicKeyAlgorithm alg, ECPoint eCPoint)
            throws InvalidKeySpecException {
        ECPublicKeySpec spec = new ECPublicKeySpec(eCPoint, getECParameterSpec(alg));
        return (ECPublicKey) keyFact.generatePublic(spec);
    }

    protected ECParameterSpec getECParameterSpec(PublicKeyAlgorithm alg) {
        if (alg == PublicKeyAlgorithm.ecdsa_nistp256_with_sha256 || alg == PublicKeyAlgorithm.ecies_nistp256) {
            return ecNistP256Spec;
        }
        throw new IllegalArgumentException("Unsupported EC Algorithm: " + alg);
    }

    protected BCECPublicKey convertECPublicKeyToBCECPublicKey(PublicKeyAlgorithm alg,
            java.security.interfaces.ECPublicKey ecPublicKey) throws InvalidKeySpecException {
        if (ecPublicKey instanceof BCECPublicKey) {
            return (BCECPublicKey) ecPublicKey;
        }

        org.bouncycastle.math.ec.ECPoint ecPoint = EC5Util.convertPoint(getECCurve(alg), ecPublicKey.getW(), false);
        ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, getECParameterSpec(alg));

        return (BCECPublicKey) keyFact.generatePublic(keySpec);

    }

    protected EccPoint getVerificationKey(Certificate signerCert) throws IllegalArgumentException {
        for (SubjectAttribute attr : signerCert.getSubjectAttributes()) {
            if (attr.getSubjectAttributeType() == SubjectAttributeType.verification_key) {
                return attr.getPublicKey().getPublicKey();
            }
        }
        throw new IllegalArgumentException("Couldn't retrieve verification key from signer certificate.");
    }

    protected org.certificateservices.custom.c2x.its.datastructs.basic.PublicKey getEncryptionKey(Certificate cert)
            throws IllegalArgumentException {
        for (SubjectAttribute attr : cert.getSubjectAttributes()) {
            if (attr.getSubjectAttributeType() == SubjectAttributeType.encryption_key) {
                return attr.getPublicKey();
            }
        }
        throw new IllegalArgumentException("Couldn't retrieve encryption key from certificate.");
    }

    protected byte[] serializeCertWithoutSignature(Certificate certificate) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.write(certificate.getVersion());
        SerializationHelper.encodeVariableSizeVector(dos, certificate.getSignerInfos());
        certificate.getSubjectInfo().serialize(dos);
        SerializationHelper.encodeVariableSizeVector(dos, certificate.getSubjectAttributes());
        SerializationHelper.encodeVariableSizeVector(dos, certificate.getValidityRestrictions());
        return baos.toByteArray();
    }

    protected byte[] serializeDataToBeSignedInSecuredMessage(SecuredMessage message, Signature signature)
            throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.write(message.getProtocolVersion());
        dos.write(message.getSecurityProfile());

        SerializationHelper.encodeVariableSizeVector(dos, message.getHeaderFields());

        serializeTotalPayloadSize(dos, message.getPayloadFields());
        for (Payload pl : message.getPayloadFields()) {
            if (pl.getPayloadType() == PayloadType.encrypted || pl.getPayloadType() == PayloadType.unsecured) {
                // if payload shouldn't be included in the signature should only the type and length be included
                dos.write(pl.getPayloadType().getByteValue());
                IntX size = new IntX(pl.getData().length);
                size.serialize(dos);
            } else {
                pl.serialize(dos);
            }
        }

        // Serialize all trailer fields until a signature is found
        serializeTotalSignedTrailerLength(dos, message.getTrailerFields(), signature);
        for (TrailerField tf : message.getTrailerFields()) {
            if (tf.getTrailerFieldType() != TrailerFieldType.signature) {
                tf.serialize(dos);
            } else {
                // Don't calculate any more fields in the signature.
                break;
            }
        }

        byte[] retval = baos.toByteArray();
        return retval;
    }

    protected SignerInfo findFirstValidSignerInfo(Certificate certificate) {
        for (SignerInfo si : certificate.getSignerInfos()) {
            SignerInfoType type = si.getSignerInfoType();
            if (type == SignerInfoType.self || type == SignerInfoType.certificate
                    || type == SignerInfoType.certificate_chain) {
                return si;
            }
        }
        throw new IllegalArgumentException(
                "No signer info of type self, certificate or certificate_chain found when verifying certificate");
    }

    protected Certificate findFirstValidCertificateInMessage(SecuredMessage msg) {
        for (HeaderField hf : msg.getHeaderFields()) {
            if (hf.getHeaderFieldType() == HeaderFieldType.signer_info) {
                SignerInfo si = hf.getSigner();
                if (si.getSignerInfoType() == SignerInfoType.certificate) {
                    return si.getCertificate();
                }
            }
        }
        throw new IllegalArgumentException(
                "No signer info of type certificate or certificate_chain found when verifying secured message");
    }

    protected Signature findSignatureInMessage(SecuredMessage message) throws IllegalArgumentException {
        if (message.getTrailerFields() != null) {
            for (TrailerField tf : message.getTrailerFields()) {
                if (tf.getTrailerFieldType() == TrailerFieldType.signature) {
                    return tf.getSignature();
                }
            }
        }
        throw new IllegalArgumentException("No signature trailer field found in secured message.");
    }

    protected void serializeTotalPayloadSize(DataOutputStream out,
            List<? extends StructSerializer> variableSizeVector) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        for (StructSerializer next : variableSizeVector) {
            next.serialize(dos);
        }
        byte[] data = baos.toByteArray();
        IntX size = new IntX(data.length);
        size.serialize(out);
    }

    protected void serializeTotalSignedTrailerLength(DataOutputStream out, List<TrailerField> variableSizeVector,
            Signature signature) throws IOException, IllegalArgumentException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        for (TrailerField next : variableSizeVector) {
            if (next.getTrailerFieldType() == TrailerFieldType.signature) {
                break;
            }
            next.serialize(dos);
        }
        byte[] data = baos.toByteArray();
        int length = data.length;
        length += calculateSignatureLength(signature);
        IntX size = new IntX(length);
        size.serialize(out);
    }

    protected int calculateSignatureLength(Signature signature) throws IllegalArgumentException {
        if (signature.getPublicKeyAlgorithm() == PublicKeyAlgorithm.ecdsa_nistp256_with_sha256) {
            if (signature.getSignatureValue().getR().getEccPointType() == EccPointType.uncompressed) {
                return 2 + (3 * signature.getPublicKeyAlgorithm().getFieldSize()); // public key alg type (1 byte) + EccPointType (1 byte) + R (2 * (x+y) field size in bytes) + s (field size in bytes)
            }
            return 2 + (2 * signature.getPublicKeyAlgorithm().getFieldSize()); // public key alg type (1 byte) + EccPointType (1 byte) + R (field size in bytes) + s (field size in bytes)
        }
        throw new IllegalArgumentException(
                "Error unsupported digital signature algorithm " + signature.getPublicKeyAlgorithm());
    }

}