public class AmCardHandler {

	private Card card = null;;
	private CardChannel channel = null;
	private SecureMessaging sm = null;
	private boolean connected = false;
	
	static Logger logger = Logger.getLogger(AmCardHandler.class);

	/**
	 * Sendet die übergebene CommandAPDU an die konnektierte Karte. 
	 * Falls SecureMessaging initilisiert und gesetzt ist wird die APDU vor dem
	 * Senden SM-geschützt. Die empfangende APDU wird SM befreit und zurückgegeben.
	 * @param capdu Plain Command-APDU
	 * @return plain Response-APDU
	 * @throws SecureMessagingException 
	 * @throws CardException 
	 */
	public ResponseAPDU transceive(CommandAPDU capdu) throws SecureMessagingException, CardException  {
		
		logger.debug("plain C-APDU:\n" + HexString.bufferToHex(capdu.getBytes()));
		
		if (sm != null)	{ 
			capdu = sm.wrap(capdu);
			logger.debug("potected C-APDU:\n"+ HexString.bufferToHex(capdu.getBytes()));
		}
		
		ResponseAPDU resp = channel.transmit(capdu);

		if (sm != null) {
			logger.debug("potected R-APDU:\n"+ HexString.bufferToHex(resp.getBytes()));
			resp = sm.unwrap(resp);
		}
		
		logger.debug("plain R-APDU:\n"+ HexString.bufferToHex(resp.getBytes()));
		
		return resp;
	}
	

	/**
	 * Aktiviert das SecureMessaging für alle nachfolgenden transceive-Aufrufe.
	 * @param sm initialisiertes SecureMessaging-Objekt
	 */
	public void setSecureMessaging(SecureMessaging sm) {
		this.sm = sm;
	}

	/**
	 * Establish connection to terminal and card on terminal.
	 * 
	 * @param index
	 *            Number of the terminal to use
	 * @return connect Connection successfull ?
	 * @throws CardException
	 */
	public boolean connect(int index) throws CardException {
		
		/* Is a Reader connected we can access? */
		if (TerminalFactory.getDefault().terminals().list().size() == 0) {
			logger.error("No reader available");
			throw new CardException("No reader available");
		}

		/* Terminal we are working on */
		CardTerminal terminal = TerminalFactory.getDefault().terminals().list().get(index);

		/* Is a card present? */
		if (!terminal.isCardPresent()) {
			logger.error("No card available");
			throw new CardException("No card available");
		}

		card = terminal.connect("T=1");
		channel = card.getBasicChannel();
		connected = true;
		return connected;
	}

	public void disconnect() throws CardException {
		channel.close();
		card.disconnect(true);
		connected = false;
		
	}

	public byte[] getATR() {
		return card.getATR().getBytes();
	}

} public class AmDHPublicKey extends AmPublicKey implements DHPublicKey{
	
	private static final long serialVersionUID = 5691151250780854614L;

	private final String algorithm = "DH";
	private final String format = "CVC";
	
	private DERTaggedObject p = null;
	private DERTaggedObject g = null;
	private DERTaggedObject q = null;
	private DERTaggedObject y = null;

	/**
	 * @param seq
	 */
	public AmDHPublicKey(ASN1Sequence seq) {
		super(seq);
		decode(seq);
	}
	
	public AmDHPublicKey(String oidString, BigInteger p, BigInteger q, BigInteger g, BigInteger y) {
		super(oidString);
		this.p = new DERTaggedObject(false, 1, new ASN1Integer(p));
		this.q = new DERTaggedObject(false, 2, new ASN1Integer(q));
		this.g = new DERTaggedObject(false, 3, new ASN1Integer(g));
		this.y = new DERTaggedObject(false, 4, new ASN1Integer(y));
		vec.add(this.p);
		vec.add(this.q);
		vec.add(this.g);
		vec.add(this.y);
	}
	
	/**
	 * Konstruktor für Ephemeral Public Key (TR-03110 V2.05 D.3.4)
	 * @param oidString OID
	 * @param y public value
	 */
	public AmDHPublicKey(String oidString, BigInteger y) {
		super(oidString);
		this.y = new DERTaggedObject(false, 4, new ASN1Integer(y));
		vec.add(this.y);
	}

	/* (non-Javadoc)
	 * @see java.security.Key#getAlgorithm()
	 */
	@Override
	public String getAlgorithm() {
		return algorithm;
	}

	/* (non-Javadoc)
	 * @see java.security.Key#getFormat()
	 */
	@Override
	public String getFormat() {
		return format;
	}

	/* (non-Javadoc)
	 * @see java.security.Key#getEncoded()
	 */
	@Override
	public byte[] getEncoded() {
		try {
			return super.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see de.tsenger.animamea.asn1.AmPublicKey#decode(org.bouncycastle.asn1.DERSequence)
	 */
	@Override
	protected void decode(ASN1Sequence seq) {
		for (int i = 1; i<seq.size(); i++) {
			DERTaggedObject to = (DERTaggedObject) seq.getObjectAt(i);
			switch(to.getTagNo()) {
			case 1: p  = to; break;
			case 2: q = to; break;
			case 3: g = to; break;
			case 4: y = to; break;
			}
		}	

	}
	
	/** Returns prime modulus p
	 * @return
	 */
	public BigInteger getP() {
		if (p==null) return null;
		ASN1Integer derInt = null;
		derInt = (ASN1Integer) p.getObjectParser(BERTags.INTEGER, false);
		return derInt.getPositiveValue();
	}
	
	/** Returns generator g
	 * @return
	 */
	public BigInteger getG() {
		if (g==null) return null;
		ASN1Integer derInt = null;
		derInt = (ASN1Integer) g.getObjectParser(BERTags.INTEGER, false);
		return derInt.getPositiveValue();
	}
	
	/** Returns oder of the subgroup q
	 * @return
	 */
	public BigInteger getQ() {
		if (q==null) return null;
		ASN1Integer derInt = null;
		derInt = (ASN1Integer) q.getObjectParser(BERTags.INTEGER, false);
		return derInt.getPositiveValue();
	}

	/* (non-Javadoc)
	 * @see javax.crypto.interfaces.DHPublicKey#getY()
	 */
	@Override
	public BigInteger getY() {
		if (y==null) return null;
		ASN1Integer derInt = null;
		derInt = (ASN1Integer) y.getObjectParser(BERTags.INTEGER, false);
		return derInt.getPositiveValue();
	}

	/* (non-Javadoc)
	 * @see javax.crypto.interfaces.DHKey#getParams()
	 */
	@Override
	public DHParameterSpec getParams() {
		DHParameterSpec dhSpec = new DHParameterSpec(getP(), getG());
		return dhSpec;
	}

} public class AmECPublicKey extends AmPublicKey implements ECPublicKey{
	
	private static final long serialVersionUID = 3574151885727849955L;
	
	private final String algorithm = "EC";
	private final String format = "CVC";
	
	private DERTaggedObject p = null;
	private DERTaggedObject a = null;
	private DERTaggedObject b = null;
	private DERTaggedObject G = null;
	private DERTaggedObject r = null;
	private DERTaggedObject Y = null;
	private DERTaggedObject f = null;
	
	/**
	 * Konstruktor zum Dekodieren der übergebenen Sequenz
	 * @param seq
	 */
	public AmECPublicKey(ASN1Sequence seq) {
		super(seq);
		decode(seq);
	}
	
	public AmECPublicKey(String oidString, BigInteger p, BigInteger a, BigInteger b, ECPoint G, BigInteger r, ECPoint Y, BigInteger f ) {
		super(oidString);
		this.p = new DERTaggedObject(false, 1, new ASN1Integer(p));
		this.a = new DERTaggedObject(false, 2, new ASN1Integer(a));
		this.b = new DERTaggedObject(false, 3, new ASN1Integer(b));
		this.G = new DERTaggedObject(false, 4, new DEROctetString(G.getEncoded(false)));
		this.r = new DERTaggedObject(false, 5, new ASN1Integer(r));
		this.Y = new DERTaggedObject(false, 6, new DEROctetString(Y.getEncoded(false)));
		this.f = new DERTaggedObject(false, 7, new ASN1Integer(f));
		vec.add(this.p);
		vec.add(this.a);
		vec.add(this.b);
		vec.add(this.G);
		vec.add(this.r);
		vec.add(this.Y);
		vec.add(this.f);
	}
	
	
	/**
	 * Konstruktor für Ephemeral Public Keys (TR-03110 V2.05 D.3.4)
	 * @param oidString OID String
	 * @param Y public point
	 */
	public AmECPublicKey(String oidString, ECPoint Y) {
		super(oidString);
		this.Y = new DERTaggedObject(false, 6, new DEROctetString(Y.getEncoded(false)));
		vec.add(this.Y);
	}

	/* (non-Javadoc)
	 * @see java.security.Key#getAlgorithm()
	 */
	@Override
	public String getAlgorithm() {
		return algorithm;
	}

	/* (non-Javadoc)
	 * @see java.security.Key#getFormat()
	 */
	@Override
	public String getFormat() {
		return format;
	}
	
	
	/** Returns prime modulus p
	 * @return
	 */
	public BigInteger getP() {
		if (p==null) return null;
		ASN1Integer derInt = ASN1Integer.getInstance(p, false);
		return derInt.getPositiveValue();
	}
	
	/** Returns first coefficient a
	 * @return
	 */
	public BigInteger getA() {
		if (a==null) return null;
		ASN1Integer derInt = ASN1Integer.getInstance(a, false);
		return derInt.getPositiveValue();
	}
	
	/** Returns second coefficient b
	 * @return
	 */
	public BigInteger getB() {
		if (b==null) return null;
		ASN1Integer derInt = ASN1Integer.getInstance(b, false);
		return derInt.getPositiveValue();
	}
	
	/** Returns base point G
	 * @return
	 */
	public byte[] getG() {
		if (G==null) return null;
		DEROctetString ostr = (DEROctetString) DEROctetString.getInstance(G, false);
		return ostr.getOctets();
	}
	
	/** Returns order of the base point r
	 * @return
	 */
	public BigInteger getR() {
		if (r==null) return null;
		ASN1Integer derInt = ASN1Integer.getInstance(r, false);
		return derInt.getPositiveValue();
	}
	
	/** Returns public point Y
	 * @return
	 */
	public byte[] getY() {
		if (Y==null) return null;
		DEROctetString ostr = (DEROctetString) DEROctetString.getInstance(Y, false);
		return ostr.getOctets();
	}
	
	/** Returns cofactor f
	 * @return
	 */
	public BigInteger getF() {
		if (f==null) return null;
		ASN1Integer derInt = ASN1Integer.getInstance(f, false);
		return derInt.getPositiveValue();
	}


	/* (non-Javadoc)
	 * @see java.security.Key#getEncoded()
	 */
	@Override
	public byte[] getEncoded() {
		try {
			return super.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see de.tsenger.animamea.asn1.AmPublicKey#decode(org.bouncycastle.asn1.DERSequence)
	 */
	@Override
	protected void decode(ASN1Sequence seq) {
	
		for (int i = 1; i<seq.size(); i++) {
			DERTaggedObject to = (DERTaggedObject) seq.getObjectAt(i);
			switch(to.getTagNo()) {
			case 1: p = to; vec.add(p); break;
			case 2: a = to; vec.add(a); break;
			case 3: b = to; vec.add(b); break;
			case 4: G = to; vec.add(G); break;
			case 5: r = to; vec.add(r); break;
			case 6: Y = to; vec.add(Y); break;
			case 7: f = to; vec.add(f); break;
			}
		}		
	}

	/* (non-Javadoc)
	 * @see org.bouncycastle.jce.interfaces.ECKey#getParameters()
	 */
	@Override
	public ECParameterSpec getParameters() {
		ECCurve.Fp curve = new ECCurve.Fp(getP(), getA(), getB());
		ECPoint pointG = byteArrayToECPoint(getG(), curve);	
		ECParameterSpec ecParameterSpec = new ECParameterSpec(curve, pointG, getR(), getF());
		return ecParameterSpec;
	}

	/*
	 * Returns der Public Point (namend Y in BSI TR-03110) 
	 *
	 * @see org.bouncycastle.jce.interfaces.ECPublicKey#getQ()
	 */
	@Override
	public org.bouncycastle.math.ec.ECPoint getQ() {
		ECCurve.Fp curve = new ECCurve.Fp(getP(), getA(), getB());
		ECPoint pointY = byteArrayToECPoint(getY(), curve);	
		return pointY;
	}
	
	public static AmECPublicKey getInstance(byte[] bytes) throws IOException {
		DERApplicationSpecific seq = DERApplicationSpecific.getInstance(bytes);
		AmECPublicKey ecPubKey = new AmECPublicKey(ASN1Sequence.getInstance(seq.getObject(16)));
		return ecPubKey;
	}

} public interface BSIObjectIdentifiers {

	public static final ASN1ObjectIdentifier bsi_de = new ASN1ObjectIdentifier("0.4.0.127.0.7");
	
	/** Algorithms **/	
	public static final ASN1ObjectIdentifier algorithms = bsi_de.branch("1");	
	
	// ECC	
	public static final ASN1ObjectIdentifier id_ecc = algorithms.branch("1");
	
	public static final ASN1ObjectIdentifier keyType = id_ecc.branch("2");
	public static final ASN1ObjectIdentifier ecTLVKeyFormat = keyType.branch("2");
	public static final ASN1ObjectIdentifier ecTLVPublicKey = ecTLVKeyFormat.branch("1");
	public static final ASN1ObjectIdentifier ecPSPublicKey =  keyType.branch("3");
	
	public static final ASN1ObjectIdentifier signatures = id_ecc.branch("4");
	
	public static final ASN1ObjectIdentifier ecdsa_plain_signatures = signatures.branch("1");
	public static final ASN1ObjectIdentifier ecdsa_plain_SHA1 = ecdsa_plain_signatures.branch("1");
	public static final ASN1ObjectIdentifier ecdsa_plain_SHA224 = ecdsa_plain_signatures.branch("2");
	public static final ASN1ObjectIdentifier ecdsa_plain_SHA256 = ecdsa_plain_signatures.branch("3");
	public static final ASN1ObjectIdentifier ecdsa_plain_SHA384 = ecdsa_plain_signatures.branch("4");
	public static final ASN1ObjectIdentifier ecdsa_plain_SHA512 = ecdsa_plain_signatures.branch("5");
	public static final ASN1ObjectIdentifier ecdsa_plain_RIPEMD160 = ecdsa_plain_signatures.branch("6");
	
	public static final ASN1ObjectIdentifier ecgdsa_plain_signatures = signatures.branch("2");
	public static final ASN1ObjectIdentifier ecgdsa_plain_SHA224 = ecdsa_plain_signatures.branch("1");
	public static final ASN1ObjectIdentifier ecgdsa_plain_SHA256 = ecdsa_plain_signatures.branch("2");
	public static final ASN1ObjectIdentifier ecgdsa_plain_SHA384 = ecdsa_plain_signatures.branch("3");
	public static final ASN1ObjectIdentifier ecgdsa_plain_SHA512 = ecdsa_plain_signatures.branch("4");
	
	public static final ASN1ObjectIdentifier ecschnorr_plain_signatures = signatures.branch("3");
	public static final ASN1ObjectIdentifier ecschnorr_plain_SHA224 = ecdsa_plain_signatures.branch("1");
	public static final ASN1ObjectIdentifier ecschnorr_plain_SHA256 = ecdsa_plain_signatures.branch("2");
	public static final ASN1ObjectIdentifier ecschnorr_plain_SHA384 = ecdsa_plain_signatures.branch("3");
	public static final ASN1ObjectIdentifier ecschnorr_plain_SHA512 = ecdsa_plain_signatures.branch("4");
	
	public static final ASN1ObjectIdentifier key_establishment = id_ecc.branch("5");
	
	public static final ASN1ObjectIdentifier ecka_eg = key_establishment.branch("1");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF = ecka_eg.branch("1");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF_SHA1 = ecka_eg_X963KDF.branch("1");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF_SHA224 = ecka_eg_X963KDF.branch("2");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF_SHA256 = ecka_eg_X963KDF.branch("3");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF_SHA384 = ecka_eg_X963KDF.branch("4");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF_SHA512 = ecka_eg_X963KDF.branch("5");
	public static final ASN1ObjectIdentifier ecka_eg_X963KDF_RIPEMD160 = ecka_eg_X963KDF.branch("6");

	public static final ASN1ObjectIdentifier ecka_eg_SessionKDF = ecka_eg.branch("2");
	public static final ASN1ObjectIdentifier ecka_eg_SessionKDF_3DES = ecka_eg_SessionKDF.branch("1");
	public static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES128 = ecka_eg_SessionKDF.branch("2");
	public static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES192 = ecka_eg_SessionKDF.branch("3");
	public static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES256 = ecka_eg_SessionKDF.branch("4");

	public static final ASN1ObjectIdentifier ecka_dh = key_establishment.branch("2");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF = ecka_dh.branch("1");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF_SHA1 = ecka_dh_X963KDF.branch("1");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF_SHA224 = ecka_dh_X963KDF.branch("2");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF_SHA256 = ecka_dh_X963KDF.branch("3");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF_SHA384 = ecka_dh_X963KDF.branch("4");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF_SHA512 = ecka_dh_X963KDF.branch("5");
	public static final ASN1ObjectIdentifier ecka_dh_X963KDF_RIPEMD160 = ecka_dh_X963KDF.branch("6");

	public static final ASN1ObjectIdentifier ecka_dh_SessionKDF = ecka_dh.branch("2");
	public static final ASN1ObjectIdentifier ecka_dh_SessionKDF_3DES = ecka_dh_SessionKDF.branch("1");
	public static final ASN1ObjectIdentifier ecka_dh_SessionKDF_AES128 = ecka_dh_SessionKDF.branch("2");
	public static final ASN1ObjectIdentifier ecka_dh_SessionKDF_AES192 = ecka_dh_SessionKDF.branch("3");
	public static final ASN1ObjectIdentifier ecka_dh_SessionKDF_AES256 = ecka_dh_SessionKDF.branch("4");

	public static final ASN1ObjectIdentifier id_PACE_KA = key_establishment.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_KA_GM = id_PACE_KA.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_KA_GM_SessionKDF_3DES = id_PACE_KA_GM.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_KA_GM_SessionKDF_AES128 = id_PACE_KA_GM.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_KA_GM_SessionKDF_AES192 = id_PACE_KA_GM.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_KA_GM_SessionKDF_AES256 = id_PACE_KA_GM.branch("4");
	
	// Standardized Domain Parameters
	public static final ASN1ObjectIdentifier standardizedDomainParameters = algorithms.branch("2");

	// Symmetric Ciphers
	public static final ASN1ObjectIdentifier symmetricCiphers = algorithms.branch("3");
	public static final ASN1ObjectIdentifier AES = symmetricCiphers.branch("1");
	
	public static final ASN1ObjectIdentifier authEncr = AES.branch("1");
	public static final ASN1ObjectIdentifier id_aes_CBC_CMAC_Param = authEncr.branch("1");
	public static final ASN1ObjectIdentifier id_aes_CBC_CMAC_128 = authEncr.branch("2");
	public static final ASN1ObjectIdentifier id_aes_CBC_CMAC_192 = authEncr.branch("3");
	public static final ASN1ObjectIdentifier id_aes_CBC_CMAC_256 = authEncr.branch("4");

	
	/** Protocols **/
	public static final ASN1ObjectIdentifier protocols = bsi_de.branch("2");
	
	// Internet
	public static final ASN1ObjectIdentifier internet = protocols.branch("1");
	public static final ASN1ObjectIdentifier LDAP = internet.branch("1");
	public static final ASN1ObjectIdentifier objectClasses = LDAP.branch("1");
	public static final ASN1ObjectIdentifier countryExt = LDAP.branch("2");
	public static final ASN1ObjectIdentifier serialNoExt = LDAP.branch("3");
	
	// Smartcard
	public static final ASN1ObjectIdentifier smartcard = protocols.branch("2");

	// PK
	public static final ASN1ObjectIdentifier PK = smartcard.branch("1");
	
	public static final ASN1ObjectIdentifier PK_DH =   PK.branch("1");
	public static final ASN1ObjectIdentifier PK_ECDH = PK.branch("2");
	public static final ASN1ObjectIdentifier PS_PK =   PK.branch("3");
	public static final ASN1ObjectIdentifier PS_PK_DH_Schnorr = PS_PK.branch("1");
	public static final ASN1ObjectIdentifier PS_PK_ECDH_ECSchnorr = PS_PK.branch("2");
	
	// Terminal Authentication 
	public static final ASN1ObjectIdentifier id_TA = smartcard.branch("2");

	public static final ASN1ObjectIdentifier id_TA_RSA = id_TA.branch("1");
	public static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_1 = id_TA_RSA.branch("1");
	public static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
	public static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_1 = id_TA_RSA.branch("3");
	public static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_256 = id_TA_RSA.branch("4");
	public static final ASN1ObjectIdentifier id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
	public static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_512 = id_TA_RSA.branch("6");

	public static final ASN1ObjectIdentifier id_TA_ECDSA = id_TA.branch("2");
	public static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_1 = id_TA_ECDSA.branch("1");
	public static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_224 = id_TA_ECDSA.branch("2");
	public static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_256 = id_TA_ECDSA.branch("3");
	public static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_384 = id_TA_ECDSA.branch("4");
	public static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_512 = id_TA_ECDSA.branch("5");
	
	// Chip Authentication 
	public static final ASN1ObjectIdentifier id_CA = smartcard.branch("3");

	public static final ASN1ObjectIdentifier id_CA_DH = id_CA.branch("1");
	public static final ASN1ObjectIdentifier id_CA_DH_3DES_CBC_CBC = id_CA_DH.branch("1");
	public static final ASN1ObjectIdentifier id_CA_DH_AES_CBC_CMAC_128 = id_CA_DH.branch("2");
	public static final ASN1ObjectIdentifier id_CA_DH_AES_CBC_CMAC_192 = id_CA_DH.branch("3");
	public static final ASN1ObjectIdentifier id_CA_DH_AES_CBC_CMAC_256 = id_CA_DH.branch("4");

	public static final ASN1ObjectIdentifier id_CA_ECDH = id_CA.branch("2");
	public static final ASN1ObjectIdentifier id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
	public static final ASN1ObjectIdentifier id_CA_ECDH_AES_CBC_CMAC_128 = id_CA_ECDH.branch("2");
	public static final ASN1ObjectIdentifier id_CA_ECDH_AES_CBC_CMAC_192 = id_CA_ECDH.branch("3");
	public static final ASN1ObjectIdentifier id_CA_ECDH_AES_CBC_CMAC_256 = id_CA_ECDH.branch("4");
		
	// id_PACE
	public static final ASN1ObjectIdentifier id_PACE = smartcard.branch("4");

	public static final ASN1ObjectIdentifier id_PACE_DH_GM = id_PACE.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_DH_GM_3DES_CBC_CBC = id_PACE_DH_GM.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_DH_GM_AES_CBC_CMAC_128 = id_PACE_DH_GM.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_DH_GM_AES_CBC_CMAC_192 = id_PACE_DH_GM.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_DH_GM_AES_CBC_CMAC_256 = id_PACE_DH_GM.branch("4");

	public static final ASN1ObjectIdentifier id_PACE_ECDH_GM = id_PACE.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_GM_3DES_CBC_CBC = id_PACE_ECDH_GM.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_GM_AES_CBC_CMAC_128 = id_PACE_ECDH_GM.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_GM_AES_CBC_CMAC_192 = id_PACE_ECDH_GM.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_GM_AES_CBC_CMAC_256 = id_PACE_ECDH_GM.branch("4");

	public static final ASN1ObjectIdentifier id_PACE_DH_IM = id_PACE.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_DH_IM_3DES_CBC_CBC = id_PACE_DH_IM.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_DH_IM_AES_CBC_CMAC_128 = id_PACE_DH_IM.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_DH_IM_AES_CBC_CMAC_192 = id_PACE_DH_IM.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_DH_IM_AES_CBC_CMAC_256 = id_PACE_DH_IM.branch("4");

	public static final ASN1ObjectIdentifier id_PACE_ECDH_IM = id_PACE.branch("4");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_IM_3DES_CBC_CBC = id_PACE_ECDH_IM.branch("1");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_IM_AES_CBC_CMAC_128 = id_PACE_ECDH_IM.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_IM_AES_CBC_CMAC_192 = id_PACE_ECDH_IM.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_IM_AES_CBC_CMAC_256 = id_PACE_ECDH_IM.branch("4");

	public static final ASN1ObjectIdentifier id_PACE_ECDH_CAM = id_PACE.branch("6");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_CAM_AES_CBC_CMAC_128 = id_PACE_ECDH_IM.branch("2");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_CAM_AES_CBC_CMAC_192 = id_PACE_ECDH_IM.branch("3");
	public static final ASN1ObjectIdentifier id_PACE_ECDH_CAM_AES_CBC_CMAC_256 = id_PACE_ECDH_IM.branch("4");

	// Restricted Identification
	public static final ASN1ObjectIdentifier id_RI = smartcard.branch("5");

	public static final ASN1ObjectIdentifier id_RI_DH = id_RI.branch("1");
	public static final ASN1ObjectIdentifier id_RI_DH_SHA_1 = id_RI_DH.branch("1");
	public static final ASN1ObjectIdentifier id_RI_DH_SHA_224 = id_RI_DH.branch("2");
	public static final ASN1ObjectIdentifier id_RI_DH_SHA_256 = id_RI_DH.branch("3");
	public static final ASN1ObjectIdentifier id_RI_DH_SHA_384 = id_RI_DH.branch("4");
	public static final ASN1ObjectIdentifier id_RI_DH_SHA_512 = id_RI_DH.branch("5");

	public static final ASN1ObjectIdentifier id_RI_ECDH = id_RI.branch("2");
	public static final ASN1ObjectIdentifier id_RI_ECDH_SHA_1 = id_RI_ECDH.branch("1");
	public static final ASN1ObjectIdentifier id_RI_ECDH_SHA_224 = id_RI_ECDH.branch("2");
	public static final ASN1ObjectIdentifier id_RI_ECDH_SHA_256 = id_RI_ECDH.branch("3");
	public static final ASN1ObjectIdentifier id_RI_ECDH_SHA_384 = id_RI_ECDH.branch("4");
	public static final ASN1ObjectIdentifier id_RI_ECDH_SHA_512 = id_RI_ECDH.branch("5");

	// CardInfoLocator
	public static final ASN1ObjectIdentifier CI = smartcard.branch("6");

	// eIDSecurityInfo
	public static final ASN1ObjectIdentifier eIDSecurity = smartcard.branch("7");

	// PrivilegedTerminalInfo
	public static final ASN1ObjectIdentifier PT = smartcard.branch("8");
	
	// ECKA-EG
	public static final ASN1ObjectIdentifier ECKA_EG = smartcard.branch("9");
	public static final ASN1ObjectIdentifier ECKA_EG_REC = ECKA_EG.branch("1");
	public static final ASN1ObjectIdentifier ECKA_EG_REC_woKDF = ECKA_EG_REC.branch("1");
	public static final ASN1ObjectIdentifier ECKA_EG_INI = ECKA_EG.branch("2");
	public static final ASN1ObjectIdentifier ECKA_EG_INI_woKDF = ECKA_EG_INI.branch("1");
	
	// ECKA-DH
	public static final ASN1ObjectIdentifier ECKA_DH = smartcard.branch("10");
	public static final ASN1ObjectIdentifier ECKA_DH_woKDF = ECKA_DH.branch("1");
	
	// PS
	public static final ASN1ObjectIdentifier PS = smartcard.branch("11");
	
	public static final ASN1ObjectIdentifier PSA = PS.branch("1");
	public static final ASN1ObjectIdentifier PSA_DH_Schnorr = PSA.branch("1");
	public static final ASN1ObjectIdentifier PSA_DH_Schnorr_SHA224 = PSA_DH_Schnorr.branch("2");
	public static final ASN1ObjectIdentifier PSA_DH_Schnorr_SHA256 = PSA_DH_Schnorr.branch("3");
	public static final ASN1ObjectIdentifier PSA_DH_Schnorr_SHA384 = PSA_DH_Schnorr.branch("4");
	public static final ASN1ObjectIdentifier PSA_DH_Schnorr_SHA512 = PSA_DH_Schnorr.branch("5");
	public static final ASN1ObjectIdentifier PSA_ECDH_ECSchnorr = PSA.branch("2");
	public static final ASN1ObjectIdentifier PSA_ECDH_ECSchnorr_SHA224 = PSA_ECDH_ECSchnorr.branch("2");
	public static final ASN1ObjectIdentifier PSA_ECDH_ECSchnorr_SHA256 = PSA_ECDH_ECSchnorr.branch("3");
	public static final ASN1ObjectIdentifier PSA_ECDH_ECSchnorr_SHA384 = PSA_ECDH_ECSchnorr.branch("4");
	public static final ASN1ObjectIdentifier PSA_ECDH_ECSchnorr_SHA512 = PSA_ECDH_ECSchnorr.branch("5");
	public static final ASN1ObjectIdentifier PSM = PS.branch("2");
	public static final ASN1ObjectIdentifier PSM_DH_Schnorr = PSM.branch("1");
	public static final ASN1ObjectIdentifier PSM_DH_Schnorr_SHA224 = PSM_DH_Schnorr.branch("2");
	public static final ASN1ObjectIdentifier PSM_DH_Schnorr_SHA256 = PSM_DH_Schnorr.branch("3");
	public static final ASN1ObjectIdentifier PSM_DH_Schnorr_SHA384 = PSM_DH_Schnorr.branch("4");
	public static final ASN1ObjectIdentifier PSM_DH_Schnorr_SHA512 = PSM_DH_Schnorr.branch("5");
	public static final ASN1ObjectIdentifier PSM_ECDH_ECSchnorr = PSM.branch("2");
	public static final ASN1ObjectIdentifier PSM_ECDH_ECSchnorr_SHA224 = PSM_ECDH_ECSchnorr.branch("2");
	public static final ASN1ObjectIdentifier PSM_ECDH_ECSchnorr_SHA256 = PSM_ECDH_ECSchnorr.branch("3");
	public static final ASN1ObjectIdentifier PSM_ECDH_ECSchnorr_SHA384 = PSM_ECDH_ECSchnorr.branch("4");
	public static final ASN1ObjectIdentifier PSM_ECDH_ECSchnorr_SHA512 = PSM_ECDH_ECSchnorr.branch("5");
	public static final ASN1ObjectIdentifier PSC = PS.branch("3");
	public static final ASN1ObjectIdentifier PSC_DH_Schnorr = PSC.branch("1");
	public static final ASN1ObjectIdentifier PSC_DH_Schnorr_SHA224 = PSC_DH_Schnorr.branch("2");
	public static final ASN1ObjectIdentifier PSC_DH_Schnorr_SHA256 = PSC_DH_Schnorr.branch("3");
	public static final ASN1ObjectIdentifier PSC_DH_Schnorr_SHA384 = PSC_DH_Schnorr.branch("4");
	public static final ASN1ObjectIdentifier PSC_DH_Schnorr_SHA512 = PSC_DH_Schnorr.branch("5");
	public static final ASN1ObjectIdentifier PSC_ECDH_ECSchnorr = PSC.branch("2");
	public static final ASN1ObjectIdentifier PSC_ECDH_ECSchnorr_SHA224 = PSC_ECDH_ECSchnorr.branch("2");
	public static final ASN1ObjectIdentifier PSC_ECDH_ECSchnorr_SHA256 = PSC_ECDH_ECSchnorr.branch("3");
	public static final ASN1ObjectIdentifier PSC_ECDH_ECSchnorr_SHA384 = PSC_ECDH_ECSchnorr.branch("4");
	public static final ASN1ObjectIdentifier PSC_ECDH_ECSchnorr_SHA512 = PSC_ECDH_ECSchnorr.branch("5");
		
	// PCD Reader
	public static final ASN1ObjectIdentifier PCD = protocols.branch("3");
	
	// id_PACE PCD
	public static final ASN1ObjectIdentifier PACE_PCD = PCD.branch("4");

	public static final ASN1ObjectIdentifier PACE_PCD_DH_GM = PACE_PCD.branch("1");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_GM_3DES_CBC_CBC = PACE_PCD_DH_GM.branch("1");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_GM_AES_CBC_CMAC_128 = PACE_PCD_DH_GM.branch("2");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_GM_AES_CBC_CMAC_192 = PACE_PCD_DH_GM.branch("3");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_GM_AES_CBC_CMAC_256 = PACE_PCD_DH_GM.branch("4");

	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_GM = PACE_PCD.branch("2");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_GM_3DES_CBC_CBC = PACE_PCD_ECDH_GM.branch("1");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_GM_AES_CBC_CMAC_128 = PACE_PCD_ECDH_GM.branch("2");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_GM_AES_CBC_CMAC_192 = PACE_PCD_ECDH_GM.branch("3");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_GM_AES_CBC_CMAC_256 = PACE_PCD_ECDH_GM.branch("4");

	public static final ASN1ObjectIdentifier PACE_PCD_DH_IM = PACE_PCD.branch("3");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_IM_3DES_CBC_CBC = PACE_PCD_DH_IM.branch("1");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_IM_AES_CBC_CMAC_128 = PACE_PCD_DH_IM.branch("2");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_IM_AES_CBC_CMAC_192 = PACE_PCD_DH_IM.branch("3");
	public static final ASN1ObjectIdentifier PACE_PCD_DH_IM_AES_CBC_CMAC_256 = PACE_PCD_DH_IM.branch("4");

	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_IM = PACE_PCD.branch("4");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_IM_3DES_CBC_CBC = PACE_PCD_ECDH_IM.branch("1");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_IM_AES_CBC_CMAC_128 = PACE_PCD_ECDH_IM.branch("2");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_IM_AES_CBC_CMAC_192 = PACE_PCD_ECDH_IM.branch("3");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_IM_AES_CBC_CMAC_256 = PACE_PCD_ECDH_IM.branch("4");

	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_CAM = PACE_PCD.branch("6");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_CAM_AES_CBC_CMAC_128 = PACE_PCD_ECDH_IM.branch("2");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_CAM_AES_CBC_CMAC_192 = PACE_PCD_ECDH_IM.branch("3");
	public static final ASN1ObjectIdentifier PACE_PCD_ECDH_CAM_AES_CBC_CMAC_256 = PACE_PCD_ECDH_IM.branch("4");

	/** Applications **/
	public static final ASN1ObjectIdentifier applications = bsi_de.branch("3");
	
	// MRTD
	public static final ASN1ObjectIdentifier mrtd = applications.branch("1");

	public static final ASN1ObjectIdentifier policies = mrtd.branch("1");
	public static final ASN1ObjectIdentifier CSCA = policies.branch("1");
	public static final ASN1ObjectIdentifier CVCA = policies.branch("2");
	public static final ASN1ObjectIdentifier CVCA_ePassport = CVCA.branch("1");
	public static final ASN1ObjectIdentifier CVCA_eID = CVCA.branch("2");
	public static final ASN1ObjectIdentifier CVCA_eSign = CVCA.branch("3");
	public static final ASN1ObjectIdentifier DV =   policies.branch("3");
	public static final ASN1ObjectIdentifier DV_ePassport = DV.branch("1");
	public static final ASN1ObjectIdentifier DV_ePassport_bPol = DV_ePassport.branch("1");
	public static final ASN1ObjectIdentifier DV_eID = DV.branch("2");
	public static final ASN1ObjectIdentifier DV_eSign = DV.branch("3");

	public static final ASN1ObjectIdentifier roles = mrtd.branch("2");
	public static final ASN1ObjectIdentifier id_IS = roles.branch("1");
	public static final ASN1ObjectIdentifier id_AT = roles.branch("2");
	public static final ASN1ObjectIdentifier id_AT_eIDAccess = id_AT.branch("1");
	public static final ASN1ObjectIdentifier id_AT_specialFunctions = id_AT.branch("2");
	public static final ASN1ObjectIdentifier id_AT_eID_Biometrics = id_AT.branch("3");
	public static final ASN1ObjectIdentifier id_ST = roles.branch("3");
	
	public static final ASN1ObjectIdentifier extensions = mrtd.branch("3");
	public static final ASN1ObjectIdentifier description = extensions.branch("1");
	public static final ASN1ObjectIdentifier plainFormat = description.branch("1");
	public static final ASN1ObjectIdentifier htmlFormat = description.branch("2");
	public static final ASN1ObjectIdentifier pdfFormat = description.branch("3");
	public static final ASN1ObjectIdentifier sector = extensions.branch("2");
	public static final ASN1ObjectIdentifier PS_sector = extensions.branch("3");
	
	public static final ASN1ObjectIdentifier auxiliaryData = mrtd.branch("4");
	public static final ASN1ObjectIdentifier DateOfBirth = auxiliaryData.branch("1");
	public static final ASN1ObjectIdentifier DateOfExpiry = auxiliaryData.branch("2");
	public static final ASN1ObjectIdentifier CommunityID = auxiliaryData.branch("3");
	public static final ASN1ObjectIdentifier PSM_Message = auxiliaryData.branch("4");
	
	public static final ASN1ObjectIdentifier DefectList = mrtd.branch("5");
	public static final ASN1ObjectIdentifier AuthDefect = DefectList.branch("1");
	public static final ASN1ObjectIdentifier certRevoked = AuthDefect.branch("1");
	public static final ASN1ObjectIdentifier certReplaced = AuthDefect.branch("2");
	public static final ASN1ObjectIdentifier certChipAuthKeyRevoked = AuthDefect.branch("3");
	public static final ASN1ObjectIdentifier certActiveAuthKeyRevoked = AuthDefect.branch("4");
	public static final ASN1ObjectIdentifier ePassportDefect = DefectList.branch("2");
	public static final ASN1ObjectIdentifier ePassportDGMalformed = ePassportDefect.branch("1");
	public static final ASN1ObjectIdentifier SODInvalid = ePassportDefect.branch("2");
	public static final ASN1ObjectIdentifier COMSODDiscrepancy = ePassportDefect.branch("3");
	public static final ASN1ObjectIdentifier eIDDefect = DefectList.branch("3");
	public static final ASN1ObjectIdentifier eIDDGMalformed = eIDDefect.branch("1");
	public static final ASN1ObjectIdentifier eIDIntegrity = eIDDefect.branch("2");
	public static final ASN1ObjectIdentifier eIDSecurityInfoMissing = eIDDefect.branch("3");
	public static final ASN1ObjectIdentifier eIDDGMissing = eIDDefect.branch("4");
	public static final ASN1ObjectIdentifier DocumentDefect = DefectList.branch("4");
	public static final ASN1ObjectIdentifier CardSecurityMalformed = DocumentDefect.branch("1");
	public static final ASN1ObjectIdentifier ChipSecurityMalformed = DocumentDefect.branch("2");
	public static final ASN1ObjectIdentifier PowerDownReq = DocumentDefect.branch("3");
	public static final ASN1ObjectIdentifier DSMalformed = DocumentDefect.branch("4");
	public static final ASN1ObjectIdentifier EAC2PrivilegedTerminalInfoMissing = DocumentDefect.branch("5");

	// eID
	public static final ASN1ObjectIdentifier eID = applications.branch("2");
	public static final ASN1ObjectIdentifier SecurityObject = eID.branch("1");
	public static final ASN1ObjectIdentifier BlackList = eID.branch("2");
	
	// eCard-API
	public static final ASN1ObjectIdentifier eCardAPI = applications.branch("3");
		
	// SMGW
	public static final ASN1ObjectIdentifier SMGW = applications.branch("4");

	// digital Seal
	public static final ASN1ObjectIdentifier digitalSeal = applications.branch("4");
	

	/** PKI **/
	public static final ASN1ObjectIdentifier pki = bsi_de.branch("4");
	public static final ASN1ObjectIdentifier x509 = pki.branch("1");
	public static final ASN1ObjectIdentifier certRequest = x509.branch("1");
	public static final ASN1ObjectIdentifier rfc4211_CertReqMsgs = certRequest.branch("1");
} public class CertificateHolderAuthorizationTemplate extends ASN1Object{

	private ASN1ObjectIdentifier terminalType = null;
	private DiscretionaryData auth = null;
	private byte role;
	
	/** Constructor for Encoding a CHAT
	 * @param terminalType OID for the terminal type to use
	 * @param disData
	 */
	public CertificateHolderAuthorizationTemplate(ASN1ObjectIdentifier terminalType,	DiscretionaryData disData) {
		this.terminalType = terminalType;
		this.auth = disData;		 
	}
	
	/** Constructor for Decoding CHAT from SEQUENCE
	 * @param chatSeq
	 * @throws IOException
	 */
	public CertificateHolderAuthorizationTemplate(ASN1Sequence chatSeq) throws IOException {
		this.terminalType = (ASN1ObjectIdentifier) chatSeq.getObjectAt(0);
		
		DEROctetString oct = (DEROctetString) ((DERApplicationSpecific) chatSeq.getObjectAt(1)).getObject(BERTags.OCTET_STRING);
		this.auth = new DiscretionaryData(oct.getOctets());
		
	}


	
	public byte getRole(){
		this.role = (byte) (auth.getData()[0] & 0xc0);
		return role;
	}

	/* (non-Javadoc)
	 * @see org.bouncycastle.asn1.ASN1Object#toASN1Primitive()
	 */
	@Override
	public ASN1Primitive toASN1Primitive() {
		ASN1EncodableVector v = new ASN1EncodableVector();
		v.add(terminalType);
		v.add(auth);
 
		return new DERApplicationSpecific(0x4c, v);
	}


} public class DiscretionaryData extends ASN1Object{

	private DEROctetString data = null;
	
	/** Constructor for Encoding
	 * @param data
	 */
	public DiscretionaryData(byte[] data) {
		this.data = new DEROctetString(data);
	}

	/** Constructor for Encoding
	 * @param data
	 */
	public DiscretionaryData(byte data) {
		this.data = new DEROctetString(new byte[]{data});
	}


	/* (non-Javadoc)
	 * @see org.bouncycastle.asn1.ASN1Encodable#toASN1Object()
	 */
	@Override
	public ASN1Primitive toASN1Primitive() {
		try {
			return new DERApplicationSpecific(false, 0x13, data);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public byte[] getData() {
		return data.getOctets();
	}
	

} public class DomainParameter {
	
	private DHParameters dhParameters = null;
	private ECParameterSpec ecSpec = null;
	
	/**
	 * Extrahiert aus dem AlogorithmIdentifier standardisierte Parameter für DH oder ECDH.
	 * @param Referenz auf Standardized Domain Parameters
	 */
	public DomainParameter(int ref) {
		if (ref<0||ref>18) throw new UnsupportedOperationException("unsupported standardized Domain Parameters");
		else getParameters(ref);	
	}
	
	/**
	 * Extrahiert aus dem AlogorithmIdentifier die Parameter für DH oder ECDH.
	 * Es werden standardisierte DomainParameter und explizite DP erkannt.
	 * @param algorithm OID
	 */
	public DomainParameter(AlgorithmIdentifier aid) {
		if (aid.getAlgorithm().toString().equals(BSIObjectIdentifiers.standardizedDomainParameters.toString())) {
			int dpref = ((ASN1Integer)aid.getParameters()).getPositiveValue().intValue(); 
			getParameters(dpref);	
		} 
		
		else if (aid.getAlgorithm().toString().equals("1.2.840.10045.2.1")) {
			X9ECParameters x9ecp = X9ECParameters.getInstance(aid.getParameters());
			ecSpec = new ECParameterSpec(x9ecp.getCurve(), x9ecp.getG(), x9ecp.getN());
		} 
		
		//TODO properitäre DH Domain Parameter
		
		else throw new UnsupportedOperationException("unsupported Domain Parameters. Algorithm OID: "+aid.getAlgorithm().toString());
	}

	/**
	 * @param dpref
	 */
	private void getParameters(int dpref) {
		switch (dpref) {
		case 0:
			dhParameters = modp1024_160();
			break;
		case 1:
			dhParameters = modp2048_224();
			break;
		case 3:
			dhParameters = modp2048_256();
			break;
		case 8:
			ecSpec = ECNamedCurveTable.getParameterSpec("secp192r1");
			break;
		case 9:
			ecSpec = ECNamedCurveTable.getParameterSpec("secp192r1");
			break;
		case 10:;
			ecSpec = ECNamedCurveTable.getParameterSpec("secp224r1");
			break;
		case 11:
			ecSpec = ECNamedCurveTable.getParameterSpec("brainpoolp224r1");
			break;
		case 12:
			ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
			break;
		case 13:
			ecSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
			break;
		case 14:
			ecSpec = ECNamedCurveTable.getParameterSpec("brainpoolp320r1");
			break;
		case 15:
			ecSpec = ECNamedCurveTable.getParameterSpec("secp384r1");
			break;
		case 16:
			ecSpec = ECNamedCurveTable.getParameterSpec("brainpoolp384r1");
			break;
		case 17:
			ecSpec = ECNamedCurveTable.getParameterSpec("brainpoolp512r1");
			break;
		case 18:
			ecSpec = ECNamedCurveTable.getParameterSpec("secp521r1");
			break;
		}
	}
	
	public String getDPType() {
		if (ecSpec!=null) return "ECDH";
		else if (dhParameters!=null) return "DH";
		return null;
	}
	
	public ECParameterSpec getECParameter() {
		return ecSpec;
	}
	
	public DHParameters getDHParameter() {
		return dhParameters;
	}

} public class DynamicAuthenticationData extends ASN1Object{
	
	private final List<DERTaggedObject> objects = new ArrayList<DERTaggedObject>(3);

	
	/**
	 * Constructor for encoding
	 */
	public DynamicAuthenticationData() {
	}
	
	
	/**
	 * Constructor for decoding
	 * @param data
	 */
	public DynamicAuthenticationData(byte[] data) {

		DERApplicationSpecific das = null;
		ASN1Sequence seq = null;
		
		try {
			das = (DERApplicationSpecific) DERApplicationSpecific.fromByteArray(data);
			seq = ASN1Sequence.getInstance(das.getObject(BERTags.SEQUENCE));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


		for (int i = 0; i < seq.size(); i++) {
			DERTaggedObject temp = (DERTaggedObject) seq.getObjectAt(i);
			objects.add(temp);
		}

	}
	
	/**
	 * Fügt ein Tagged Object mit dem Tag (0x80 & tagno) ein.
	 * @param tagno
	 * @param data
	 */
	public void addDataObject(int tagno, byte[] data) {
		objects.add(new DERTaggedObject(false, tagno, new DEROctetString(data)));
	}
	
	/**
	 * Liefert den Inhalt des Tagged Objects mit dem Tag (0x80 & tagno) zurück.
	 * @param tagno
	 * @return
	 */
	public byte[] getDataObject(int tagno) {
		for (DERTaggedObject item : objects) {
			if (item.getTagNo() == tagno) {
				DEROctetString ostr;
				ostr = (DEROctetString) item.getObjectParser(BERTags.OCTET_STRING, false);
				return ostr.getOctets();
			}
		}
		return null;
	}


	/* (non-Javadoc)
	 * @see org.bouncycastle.asn1.ASN1Object#toASN1Primitive()
	 */
	@Override
	public ASN1Primitive toASN1Primitive() {
		ASN1EncodableVector asn1vec = new ASN1EncodableVector();
		
		for (DERTaggedObject item : objects) {
			asn1vec.add(item);
		}

		return new DERApplicationSpecific(0x1C, asn1vec); // Application specific + 0x1c = 0x7C
	}
} public class PaceDomainParameterInfo extends ASN1Object {

	private ASN1ObjectIdentifier protocol = null;
	private AlgorithmIdentifier domainParameter = null;
	private ASN1Integer parameterId = null;

	public PaceDomainParameterInfo(ASN1Sequence seq) {
		protocol = (ASN1ObjectIdentifier) seq.getObjectAt(0);
		domainParameter = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));

		if (seq.size() > 2) {
			parameterId = (ASN1Integer) seq.getObjectAt(2);
		}
	}

	public ASN1ObjectIdentifier getProtocol() {
		return protocol;
	}

	public AlgorithmIdentifier getDomainParameter() {
		return domainParameter;
	}

	public int getParameterId() {
		if (parameterId == null)
			return -1; // optionales Feld parameterId nicht vorhanden
		else
			return parameterId.getValue().intValue();
	}

	@Override
	public String toString() {
		return "PaceDomainParameterInfo\n\tOID: " + getProtocol()
				+ "\n\tDomainParameter: \n\t\t"
				+ getDomainParameter().getAlgorithm() + "\n\t\t"
				+ getDomainParameter().getParameters() + 
				(parameterId!=null?"\n\tParameterId: " + getParameterId() + "\n":"\n");
	}

	/**
	 * The definition of PaceDomainParameterInfo is
     * <pre>
     * PaceDomainParameterInfo ::= SEQUENCE {
     *      protocol		OBJECT IDENTIFIER(,
     *      				id-id_PACE-DH-GM |
     *      				id-id_PACE-ECDH-GM |
     *      				id-id_PACE-DH-IM |
     *      				id-id_PACE-ECDH-IM),
     *      domainParameter	AlgorithmIdentifier,
     *      parameterId		INTEGER OPTIONAL
     * }
     * </pre>
	 */
	@Override
	public ASN1Primitive toASN1Primitive() {
		ASN1EncodableVector v = new ASN1EncodableVector();
		v.add(protocol);
		v.add(domainParameter);
		if (parameterId!=null) v.add(parameterId);
		
		return ASN1Sequence.getInstance(v);
	}
} public class PaceInfo extends ASN1Object{

	private ASN1ObjectIdentifier protocol = null;
	private ASN1Integer version = null;
	private ASN1Integer parameterId = null;
	
	public PaceInfo(ASN1Sequence seq) {
		protocol = (ASN1ObjectIdentifier) seq.getObjectAt(0);
		version = (ASN1Integer) seq.getObjectAt(1);

		if (seq.size() > 2) {
			parameterId = (ASN1Integer) seq.getObjectAt(2);
		}
	}
	
	public PaceInfo(String oid, int version, int parameterId) {
		this.protocol = new ASN1ObjectIdentifier(oid);
		this.version = new ASN1Integer(version);
		this.parameterId = new ASN1Integer(parameterId);
	}

	public String getProtocolOID() {
		return protocol.toString();
	}

	public int getVersion() {
		return version.getValue().intValue();
	}

	public Integer getParameterId() {
		if (parameterId == null)
			return null;// ID nicht vorhanden
		else
			return parameterId.getValue().intValue();
	}

	@Override
	public String toString() {
		return "PaceInfo\n\tOID: " + getProtocolOID() + "\n\tVersion: "
				+ getVersion() + 
				(parameterId!=null?"\n\tParameterId: " + getParameterId() + "\n":"\n");
	}

	/**
	 * The definition of PaceInfo is
     * <pre>
     * PaceInfo ::= SEQUENCE {
     *      protocol	OBJECT IDENTIFIER(
	 *					id-id_PACE-DH-GM-3DES-CBC-CBC |
	 *					id-id_PACE-DH-GM-AES-CBC-CMAC-128 |
	 *					id-id_PACE-DH-GM-AES-CBC-CMAC-192 |
	 *					id-id_PACE-DH-GM-AES-CBC-CMAC-256 |
	 *					id-id_PACE-ECDH-GM-3DES-CBC-CBC |
	 *					id-id_PACE-ECDH-GM-AES-CBC-CMAC-128 |
	 *					id-id_PACE-ECDH-GM-AES-CBC-CMAC-192 |
	 *					id-id_PACE-ECDH-GM-AES-CBC-CMAC-256,
	 *					id-id_PACE-DH-IM-3DES-CBC-CBC |
	 *					id-id_PACE-DH-IM-AES-CBC-CMAC-128 |
	 *					id-id_PACE-DH-IM-AES-CBC-CMAC-192 |
	 *					id-id_PACE-DH-IM-AES-CBC-CMAC-256 |
	 *					id-id_PACE-ECDH-IM-3DES-CBC-CBC |
	 *					id-id_PACE-ECDH-IM-AES-CBC-CMAC-128 |
	 *					id-id_PACE-ECDH-IM-AES-CBC-CMAC-192 |
	 *					id-id_PACE-ECDH-IM-AES-CBC-CMAC-256),
     *      version		INTEGER, -- SHOULD be 2
     *      parameterId	INTEGER OPTIONAL
     * }
     * </pre>
	 */
	@Override
	public ASN1Primitive toASN1Primitive() {
		ASN1EncodableVector v = new ASN1EncodableVector();
		v.add(protocol);
		v.add(version); 
		if (parameterId!=null) v.add(parameterId);
		
		return ASN1Sequence.getInstance(v);
	}
} @SuppressWarnings("serial")
public class AmCryptoException extends Exception {

	/**
	 * 
	 */
	public AmCryptoException() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param message
	 */
	public AmCryptoException(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param cause
	 */
	public AmCryptoException(Throwable cause) {
		super(cause);
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param message
	 * @param cause
	 */
	public AmCryptoException(String message, Throwable cause) {
		super(message, cause);
		// TODO Auto-generated constructor stub
	}

} public class AmDESCrypto extends AmCryptoProvider {

	public static int blockSize = 8;
	private byte[] keyBytes;
	private KeyParameter keyP = null;
	private byte[] IV = null;
	private byte[] sscBytes = null;

	private void initCiphers(byte[] key, byte[] iv) {
		// get the keyBytes
		keyBytes = new byte[key.length];
		System.arraycopy(key, 0, keyBytes, 0, key.length);

		// get the IV
		IV = new byte[blockSize];
		System.arraycopy(iv, 0, IV, 0, iv.length);

		keyP = new KeyParameter(keyBytes);

		encryptCipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(
				new DESedeEngine()), new ISO7816d4Padding());
		decryptCipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(
				new DESedeEngine()), new ISO7816d4Padding());

		// create the IV parameter
		ParametersWithIV parameterIV = new ParametersWithIV(keyP, IV);

		encryptCipher.init(true, parameterIV);
		decryptCipher.init(false, parameterIV);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.tsenger.animamea.crypto.AmCryptoProvider#init(byte[], long)
	 */
	@Override
	public void init(byte[] keyBytes, byte[] ssc) {
		if(ssc!=null) sscBytes = ssc.clone();
		initCiphers(keyBytes, new byte[blockSize]);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.tsenger.animamea.crypto.AmCryptoProvider#getMAC(byte[])
	 */
	@Override
	public byte[] getMAC(byte[] data) {

		byte[] n = new byte[8 + data.length];
		System.arraycopy(sscBytes, 0, n, 0, 8);
		System.arraycopy(data, 0, n, 8, data.length);

		BlockCipher cipher = new DESEngine();
		Mac mac = new ISO9797Alg3Mac(cipher, 64, new ISO7816d4Padding());

		ParametersWithIV parameterIV = new ParametersWithIV(keyP, IV);

		mac.init(parameterIV);
		mac.update(n, 0, n.length);

		byte[] out = new byte[8];

		mac.doFinal(out, 0);

		return out;
	}

	/**
	 * Dekodiert einen Block mit DES
	 * 
	 * @param key
	 *            Byte-Array enthält den 3DES-Schlüssel
	 * @param z
	 *            verschlüsselter Block
	 * @return entschlüsselter block
	 */
	@Override
	public byte[] decryptBlock(byte[] key, byte[] z) {
		byte[] s = new byte[blockSize];
		KeyParameter encKey = new KeyParameter(key);
		BlockCipher cipher = new DESedeEngine();
		cipher.init(false, encKey);
		cipher.processBlock(z, 0, s, 0);
		return s;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.tsenger.animamea.crypto.AmCryptoProvider#getMAC(byte[], byte[])
	 */
	@Override
	public byte[] getMAC(byte[] key, byte[] data) {
		BlockCipher cipher = new DESEngine();
		Mac mac = new ISO9797Alg3Mac(cipher, 64, new ISO7816d4Padding());

		KeyParameter keyP = new KeyParameter(key);
		mac.init(keyP);
		mac.update(data, 0, data.length);

		byte[] out = new byte[8];

		mac.doFinal(out, 0);

		return out;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.tsenger.animamea.crypto.AmCryptoProvider#getBlockSize()
	 */
	@Override
	public int getBlockSize() {
		return blockSize;
	}
	
	/**
	 * Berechnet die XOR-Verknüpfung von zwei Bytes-Arrays der selben Länge
	 * 
	 * @param a
	 *            Byte-Array A
	 * @param b
	 *            Byte-Array B
	 * @return XOR-Verknüpfung von a und b
	 * @throws IllegalArgumentException
	 *             falls die beiden Byte-Arrays nicht die gleiche Länge haben
	 */
	public byte[] xorArray(byte[] a, byte[] b)
			throws IllegalArgumentException {
		if (b.length < a.length)
			throw new IllegalArgumentException(
					"length of byte [] b must be >= byte [] a");
		byte[] c = new byte[a.length];
		for (int i = 0; i < a.length; i++) {
			c[i] = (byte) (a[i] ^ b[i]);
		}
		return c;
	}

} public class KeyDerivationFunction {

	private byte[] mergedData = null;

	/**
	 * Constants that help in determining whether or not a byte array is parity
	 * adjusted.
	 */
	private static final byte[] PARITY = { 8, 1, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0,
			8, 0, 2, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 3, 0, 8,
			8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0,
			0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8,
			8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8,
			0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0,
			0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0,
			8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8,
			8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8,
			0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8,
			8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 4, 8, 8, 0, 8, 0, 0, 8,
			8, 0, 0, 8, 0, 8, 8, 0, 8, 5, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0,
			6, 8 };

	/**
	 * 
	 * Das MRZ-Passwort besteht aus dem SHA1-Wert der Dokumentennummer +
	 * Geburtsdatum + Gültigkeitsdatum (jeweils mit Prüfziffer)
	 * 
	 * @param documentNr
	 *            Dokumentennummer plus Prüfziffer
	 * @param dateOfBirth
	 *            Geburtsdatum aus der MRZ plus Prüfziffer
	 * @param dateOfExpiry
	 *            Gültigkeitsdatum aus der MRZ plus Prüfziffer
	 * @return K = SHA-1(Serial Number||Date of Birth||Date of Expiry)
	 */
	public static byte[] getMRZBytes(String documentNr, String dateOfBirth,
			String dateOfExpiry) {
		String mrzInfo = documentNr + dateOfBirth + dateOfExpiry;
		byte[] passwordBytes = mrzInfo.getBytes();

		byte[] K = new byte[20];

		SHA1Digest sha1 = new SHA1Digest();
		sha1.update(passwordBytes, 0, passwordBytes.length);
		sha1.doFinal(K, 0);

		return K;
	}

	/**
	 * Constructor for Key Derivation Function (KDF) siehe BSI TR-03110 Kapitel
	 * A.2.3
	 * 
	 * @param K
	 *            The shared secret Value (z.B. PIN, CAN, PUK als Byte Array
	 *            oder abgeleitete MRZ siehe BSI TR-03110 Tabelle A.4)
	 * @param c
	 *            A 32-bit, big-endian integer counter. 1 for en-/decoding, 2
	 *            for MAC (checksum), 3 for deriving encryption keys from a
	 *            password
	 * @throws IllegalArgumentException
	 *             c must be 1, 2 or 3
	 */
	public KeyDerivationFunction(byte[] K, int c)  {

		if (c <= 0 || c > 3)
			throw new IllegalArgumentException("c must be 1, 2 or 3!");

		byte[] cBytes = intToByteArray(c);

		mergedData = new byte[K.length + cBytes.length];
		System.arraycopy(K, 0, mergedData, 0, K.length);
		System.arraycopy(cBytes, 0, mergedData, K.length, cBytes.length);
	}

	/**
	 * Constructor for Key Derivation Function (KDF) siehe BSI TR-03110 Kapitel
	 * A.2.3
	 * 
	 * @param K
	 *            The shared secret Value (z.B. PIN, CAN, PUK oder abgeleitete
	 *            MRZ siehe BSI TR-03110 Tabelle A.4)
	 * @param r
	 *            a nonce r
	 * @param c
	 *            A 32-bit, big-endian integer counter. 1 for en-/decoding, 2
	 *            for MAC (checksum), 3 for deriving encryption keys from a
	 *            password
	 * @throws IllegalArgumentException
	 *             c must be 1, 2 or 3
	 */
	public KeyDerivationFunction(byte[] K, byte[] r, int c) throws IllegalArgumentException {

		if (c <= 0 || c > 3)
			throw new IllegalArgumentException("c must be 1, 2 or 3!");

		byte[] cBytes = intToByteArray(c);

		mergedData = new byte[K.length + +r.length + cBytes.length];
		System.arraycopy(K, 0, mergedData, 0, K.length);
		System.arraycopy(r, 0, mergedData, K.length, r.length);
		System.arraycopy(cBytes, 0, mergedData, K.length + r.length, cBytes.length);
	}

	/**
	 * Erzeugt 3DES Schlüssel
	 * 
	 * @return 112bit-3DES-Schlüssel in 24 Bytes mit korrekten Parity-Bits
	 */
	public byte[] getDESedeKey() {

		byte[] checksum = new byte[20];

		SHA1Digest sha1 = new SHA1Digest();
		sha1.update(mergedData, 0, mergedData.length);
		sha1.doFinal(checksum, 0);

		byte[] ka = new byte[8];
		byte[] kb = new byte[8];

		System.arraycopy(checksum, 0, ka, 0, ka.length);
		System.arraycopy(checksum, 8, kb, 0, kb.length);

		// Adjust Parity-Bits
		adjustParity(ka, 0);
		adjustParity(kb, 0);

		byte[] key = new byte[24];
		System.arraycopy(ka, 0, key, 0, 8);
		System.arraycopy(kb, 0, key, 8, 8);
		System.arraycopy(ka, 0, key, 16, 8);

		return key;

	}

	/**
	 * Erzeugt AES-128 Schlüssel
	 * 
	 * @return Schlüssel als Byte-Array
	 */
	public byte[] getAES128Key() {

		byte[] checksum = new byte[20];

		SHA1Digest sha1 = new SHA1Digest();
		sha1.update(mergedData, 0, mergedData.length);
		sha1.doFinal(checksum, 0);

		// keydata = H(K||r||c)
		// keydata sind die ersten 16 Byte der Hashfunktion über "mergedData"
		byte[] keydata = new byte[16];
		System.arraycopy(checksum, 0, keydata, 0, 16);
		return keydata;
	}

	/**
	 * Erzeugt AES-192 Schlüssel
	 * 
	 * @return Schlüssel als Byte-Array
	 */
	public byte[] getAES192Key() {

		byte[] checksum = getAES256Key();
		byte[] keydata = new byte[24];
		System.arraycopy(checksum, 0, keydata, 0, 24);
		return keydata;
	}

	/**
	 * Erzeugt AES-256 Schlüssel
	 * 
	 * @return Schlüssel als Byte-Array
	 */
	public byte[] getAES256Key() {

		byte[] checksum = new byte[32];

		SHA256Digest sha256 = new SHA256Digest();
		sha256.update(mergedData, 0, mergedData.length);
		sha256.doFinal(checksum, 0);

		return checksum;
	}

	/**
	 * Adjust the parity for a raw key array. This essentially means that each
	 * byte in the array will have an odd number of '1' bits (the last bit in
	 * each byte is unused.
	 * 
	 * @param kb
	 *            The key array, to be parity-adjusted.
	 * @param offset
	 *            The starting index into the key bytes.
	 */
	private void adjustParity(byte[] key, int offset) {
		for (int i = offset; i < 8; i++) {
			key[i] ^= (PARITY[key[i] & 0xff] == 8) ? 1 : 0;
		}
	}

	/**
	 * @param c
	 * @return
	 */
	private byte[] intToByteArray(int c) {
		// int -> byte[]
		byte[] intBytes = new byte[4];
		for (int i = 0; i < 4; ++i) {
			int shift = i << 3; // i * 8
			intBytes[3 - i] = (byte) ((c & (0xff << shift)) >>> shift);
		}
		return intBytes;
	}

} public class MSESetAT {

	public static final int setAT_PACE = 1;
	public static final int setAT_CA = 2;
	public static final int setAT_TA = 3;

	public static final int KeyReference_MRZ = 1;
	public static final int KeyReference_CAN = 2;
	public static final int KeyReference_PIN = 3;
	public static final int KeyReference_PUK = 4;

	private final byte CLASS = (byte) 0x00;
	private final byte INS = (byte) 0x22; // Instruction Byte: Message Security
											// Environment
	private byte P1=0;
	private final byte P2=(byte)0xA4;
	private byte[] do80CMR = null;
	private byte[] do83KeyReference = null;
	private byte[] do83KeyName = null;
	private byte[] do84PrivateKeyReference = null;
	private byte[] do7F4C_CHAT = null;
	private byte[] do91EphemeralPublicKEy = null;

	public MSESetAT() {}

	/**
	 * Setzt das zu verwendende Authentication Template (id_PACE, id_CA oder id_TA)
	 * 
	 * @param at
	 *            {@link de.tsenger.androsmex.pace.MSECommand.setAT_PACE},
	 *            {@link de.tsenger.androsmex.pace.MSECommand.setAT_CA},
	 *            {@link de.tsenger.androsmex.pace.MSECommand.setAT_TA}
	 */
	public void setAT(int at) {
		if (at == setAT_PACE) P1 = (byte) 0xC1;			
		if (at == setAT_CA)	P1 = (byte) 0x41;
		if (at == setAT_TA)	P1 = (byte) 0x81;
	}

	/**
	 * Setzt die OID des zu verwendenden Protokolls
	 * 
	 * @param protocol
	 *            Das zu verwendende Protokoll
	 */
	public void setProtocol(String protocol) {
		ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(protocol);
		DERTaggedObject to = new DERTaggedObject(false, 0x00, oid);
		try {
			do80CMR = to.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Setzt das Tag 0x83 (Reference of public / secret key) für id_PACE
	 * 
	 * @param kr
	 *            Referenziert das verwendete Passwort: 1: MRZ 2: CAN 3: PIN 4:
	 *            PUK
	 */
	public void setKeyReference(int kr) {
		DERTaggedObject to = new DERTaggedObject(false, 0x03, new ASN1Integer(kr));
		try {
			do83KeyReference = to.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Setzt das Tag 0x83 (Reference of public / secret key) für Terminal
	 * Authentication
	 * 
	 * @param kr
	 *            String der den Namen des Public Keys des Terminals beinhaltet
	 *            (ISO 8859-1 kodiert)
	 */
	public void setKeyReference(String kr) {
		DERTaggedObject to = new DERTaggedObject(false, 0x03, new DEROctetString(kr.getBytes()));
		try {
			do83KeyName = to.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Setzt das Tag 0x84 (Reference of a private key / Reference for computing
	 * a session key)
	 * 
	 * @param pkr
	 *            Bei id_PACE wird der Index der zu verwendenden Domain Parameter
	 *            angegeben Bei id_CA wird der Index des zu verwendenden Private
	 *            Keys angegeben Bei id_RI wird der Index des zu verwendenden
	 *            Private Keys angegeben
	 */
	public void setPrivateKeyReference(int pkr) {
		DERTaggedObject to = new DERTaggedObject(false, 0x04, new ASN1Integer(pkr));
		try {
			do84PrivateKeyReference = to.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void setAuxiliaryAuthenticatedData() throws UnsupportedOperationException {
		// TODO noch zu implementieren, Tag 0x67
		throw new UnsupportedOperationException("setAuxiliaryAuthenticationData not yet implemented!");
	}

	/**
	 * Setzt das Tag 0x91 (Ephemeral Public Key). Der PK muss bereits komprimiert 
	 * (siehe comp()-Funktion in TR-03110) sein.
	 * @param pubKey comp(ephemeral PK_PCD) -> TR-03110 A.2.2.3
	 */
	public void setEphemeralPublicKey(byte[] pubKey) {
		DERTaggedObject to = new DERTaggedObject(false, 0x11, new DEROctetString(pubKey));
		try {
			do91EphemeralPublicKEy = to.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @param chat
	 */
	public void setCHAT(CertificateHolderAuthorizationTemplate chat) {
		try {
			do7F4C_CHAT = chat.getEncoded(ASN1Encoding.DER);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Konstruiert aus den gesetzten Objekten eine MSE-Command-APDU 
	 * @return ByteArray mit MSE:SetAT APDU
	 */
	public CommandAPDU getCommandAPDU() {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		if (do80CMR != null)
			bos.write(do80CMR, 0, do80CMR.length);
		if (do83KeyReference != null)
			bos.write(do83KeyReference, 0, do83KeyReference.length);
		if (do83KeyName != null)
			bos.write(do83KeyName, 0, do83KeyName.length);
		if (do84PrivateKeyReference != null)
			bos.write(do84PrivateKeyReference, 0, do84PrivateKeyReference.length);
		if (do91EphemeralPublicKEy != null) 
			bos.write(do91EphemeralPublicKEy, 0 , do91EphemeralPublicKEy.length);
		if (do7F4C_CHAT != null)
			bos.write(do7F4C_CHAT, 0, do7F4C_CHAT.length);
		byte[] data = bos.toByteArray();

		return new CommandAPDU(CLASS, INS, P1, P2, data);
	}

} public class SecureMessaging {

	private byte[] ks_enc = null;
	private byte[] ks_mac = null;
	private byte[] ssc = null;
	private AmCryptoProvider crypto = null;
	private boolean useExtendLengthAPDUs = true;

	enum apdutype {
		case1, case2s, case2e, case3s, case3e, case4s, case4e
	};

	/**
	 * @param acp
	 *            AmDESCrypto- oder AmAESCrypto-Instanz
	 * @param ksenc
	 *            Session Key für Verschlüsselung (K_enc)
	 * @param ksmac
	 *            Session Key für Prüfsummenberechnung (K_mac)
	 * @param initialSSC
	 *            Initialer Wert des Send Sequence Counters
	 * @param useExtendLengthAPDUs
	 *            by default APDU with extended length will be used, if your
	 *            card doesn't support EL set this paramter to false
	 */
	public SecureMessaging(AmCryptoProvider acp, byte[] ksenc, byte[] ksmac, byte[] initialSSC, boolean useExtendLengthAPDUs) {
		this.crypto = acp;
		this.ks_enc = ksenc.clone();
		this.ks_mac = ksmac.clone();
		this.ssc = initialSSC.clone();
		this.useExtendLengthAPDUs = useExtendLengthAPDUs;
	}

	/**
	 * Konstruktor
	 * 
	 * @param acp
	 *            AmDESCrypto- oder AmAESCrypto-Instanz
	 * @param ksenc
	 *            Session Key für Verschlüsselung (K_enc)
	 * @param ksmac
	 *            Session Key für Prüfsummenberechnung (K_mac)
	 * @param initssc
	 *            Initialer Wert des Send Sequence Counters
	 */
	public SecureMessaging(AmCryptoProvider acp, byte[] ksenc, byte[] ksmac, byte[] initialSSC) {
		this(acp, ksenc, ksmac, initialSSC, true);
	}

	/**
	 * Erzeugt aus einer Plain-Command-APDU ohne Secure Messaging eine
	 * Command-APDU mit Secure Messaging.
	 * 
	 * @param capdu
	 *            plain Command-APDU
	 * @return CommandAPDU mit SM
	 * @throws SecureMessagingException
	 */
	public CommandAPDU wrap(CommandAPDU capdu) throws SecureMessagingException {

		byte[] header = null;
		DO97 do97 = null;
		DO85 do85 = null;
		DO87 do87 = null;
		DO8E do8E = null;

		incrementAtIndex(ssc, ssc.length - 1);

		// Mask class byte and pad command header
		header = new byte[4];

		// Die ersten 4 Bytes der CAPDU sind der Header
		System.arraycopy(capdu.getBytes(), 0, header, 0, 4);

		// Markiert das Secure Messaging im CLA-Byte
		header[0] = (byte) (header[0] | (byte) 0x0C);

		apdutype atype = getAPDUStructure(capdu);

		// build DO87
		if (atype == apdutype.case3s || atype == apdutype.case4s || atype == apdutype.case3e || atype == apdutype.case4e) {
			if ((header[1] & 0xff) % 2 == 1) {
				do85 = buildDO85(capdu.getData().clone());
			} else {
				do87 = buildDO87(capdu.getData().clone());
			}
		}

		// build DO97
		if (atype == apdutype.case2s || atype == apdutype.case4s || atype == apdutype.case2e || atype == apdutype.case4e) {
			do97 = buildDO97(capdu.getNe());
		}

		// build DO8E
		if (do85 != null) {
			do8E = buildDO8E(header, do85, do97);
		} else {
			do8E = buildDO8E(header, do87, do97);
		}

		// construct and return protected APDU
		ByteArrayOutputStream bOut = new ByteArrayOutputStream();
		try {

			if (do85 != null)
				bOut.write(do85.getEncoded());

			if (do87 != null)
				bOut.write(do87.getEncoded());

			if (do97 != null)
				bOut.write(do97.getEncoded());

			bOut.write(do8E.getEncoded());

		} catch (IOException e) {
			throw new SecureMessagingException(e);
		}
		return new CommandAPDU(header[0], header[1], header[2], header[3], bOut.toByteArray(), useExtendLengthAPDUs ? 65536 : 256);
	}

	/**
	 * Erzeugt aus einer SM geschützten Response-APDU eine plain Response-APDU
	 * ohne Secure Messaging.
	 * 
	 * @param rapdu
	 *            SM protected RAPDU
	 * @return plain RAPDU
	 * @throws SecureMessagingException
	 */
	public ResponseAPDU unwrap(ResponseAPDU rapdu) throws SecureMessagingException {

		DO87 do87 = null;
		DO99 do99 = null;
		DO8E do8E = null;

		incrementAtIndex(ssc, ssc.length - 1);

		int pointer = 0;
		byte[] rapduBytes = rapdu.getData();
		byte[] subArray = new byte[rapduBytes.length];

		while (pointer < rapduBytes.length) {
			System.arraycopy(rapduBytes, pointer, subArray, 0, rapduBytes.length - pointer);
			ASN1InputStream asn1sp = new ASN1InputStream(subArray);
			byte[] encodedBytes = null;
			try {
				encodedBytes = asn1sp.readObject().getEncoded();
				asn1sp.close();
			} catch (IOException e) {
				throw new SecureMessagingException(e);
			}

			ASN1InputStream asn1in = new ASN1InputStream(encodedBytes);
			try {
				switch (encodedBytes[0]) {
				case (byte) 0x87:
					do87 = new DO87();
					do87.fromByteArray(asn1in.readObject().getEncoded());
					break;
				case (byte) 0x99:
					do99 = new DO99();
					do99.fromByteArray(asn1in.readObject().getEncoded());
					break;
				case (byte) 0x8E:
					do8E = new DO8E();
					do8E.fromByteArray(asn1in.readObject().getEncoded());
				}
				asn1in.close();
			} catch (IOException e) {
				throw new SecureMessagingException(e);
			}

			pointer += encodedBytes.length;
		}

		if (do99 == null)
			throw new SecureMessagingException("Secure Messaging error: mandatory DO99 not found"); // DO99
																									// is
																									// mandatory
		// and only absent
		// if SM error
		// occurs

		// Construct K (SSC||DO87||DO99)
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		try {
			if (do87 != null)
				bout.write(do87.getEncoded());
			bout.write(do99.getEncoded());
		} catch (IOException e) {
			throw new SecureMessagingException(e);
		}

		crypto.init(ks_mac, ssc);
		byte[] cc = crypto.getMAC(bout.toByteArray());

		byte[] do8eData = do8E.getData();

		if (!java.util.Arrays.equals(cc, do8eData))
			throw new SecureMessagingException("Checksum is incorrect!\n Calculated CC: " + HexString.bufferToHex(cc) + "\nCC in DO8E: "
					+ HexString.bufferToHex(do8eData));

		// Decrypt DO87
		byte[] data = null;
		byte[] unwrappedAPDUBytes = null;

		if (do87 != null) {
			crypto.init(ks_enc, ssc);
			byte[] do87Data = do87.getData();
			try {
				data = crypto.decrypt(do87Data);
			} catch (AmCryptoException e) {
				throw new SecureMessagingException(e);
			}
			// Build unwrapped RAPDU
			unwrappedAPDUBytes = new byte[data.length + 2];
			System.arraycopy(data, 0, unwrappedAPDUBytes, 0, data.length);
			byte[] do99Data = do99.getData();
			System.arraycopy(do99Data, 0, unwrappedAPDUBytes, data.length, do99Data.length);
		} else
			unwrappedAPDUBytes = do99.getData().clone();

		return new ResponseAPDU(unwrappedAPDUBytes);
	}

	public void setExtendLengthSupport(boolean useExtendLengthAPDUs) {
		this.useExtendLengthAPDUs = useExtendLengthAPDUs;
	}

	/**
	 * encrypt data with KS.ENC and build DO85
	 * 
	 * @param data
	 * @return
	 * @throws SecureMessagingException
	 */
	private DO85 buildDO85(byte[] data) throws SecureMessagingException {
		crypto.init(ks_enc, ssc);
		byte[] enc_data;
		try {
			enc_data = crypto.encrypt(data);
		} catch (AmCryptoException e) {
			throw new SecureMessagingException(e);
		}
		return new DO85(enc_data);
	}

	/**
	 * encrypt data with KS.ENC and build DO87
	 * 
	 * @param data
	 * @return
	 * @throws SecureMessagingException
	 */
	private DO87 buildDO87(byte[] data) throws SecureMessagingException {

		crypto.init(ks_enc, ssc);
		byte[] enc_data;
		try {
			enc_data = crypto.encrypt(data);
		} catch (AmCryptoException e) {
			throw new SecureMessagingException(e);
		}

		return new DO87(enc_data);

	}

	private DO8E buildDO8E(byte[] header, DO85 do85, DO97 do97) throws SecureMessagingException {

		ByteArrayOutputStream m = new ByteArrayOutputStream();

		/**
		 * Verhindert doppeltes Padden des Headers: Nur wenn do87 oder do97
		 * vorhanden sind, wird der Header gepaddet. Ansonsten wird erst beim
		 * Berechnen des MAC gepaddet.
		 */
		try {
			if (do85 != null || do97 != null)
				m.write(crypto.addPadding(header));

			else
				m.write(header);

			if (do85 != null)
				m.write(do85.getEncoded());
			if (do97 != null)
				m.write(do97.getEncoded());
		} catch (IOException e) {
			throw new SecureMessagingException(e);
		}

		crypto.init(ks_mac, ssc);

		return new DO8E(crypto.getMAC(m.toByteArray()));
	}

	private DO97 buildDO97(int le) {
		return new DO97(le);
	}

	/**
	 * Bestimmt welchem Case die CAPDU enstpricht. (Siehe ISO/IEC 7816-3 Kapitel
	 * 12.1)
	 * 
	 * @return apdutype
	 */
	private apdutype getAPDUStructure(CommandAPDU capdu) {
		byte[] cardcmd = capdu.getBytes();

		if (cardcmd.length == 4)
			return apdutype.case1;
		if (cardcmd.length == 5)
			return apdutype.case2s;
		if (cardcmd.length == (5 + (cardcmd[4] & 0xff)) && cardcmd[4] != 0)
			return apdutype.case3s;
		if (cardcmd.length == (6 + (cardcmd[4] & 0xff)) && cardcmd[4] != 0)
			return apdutype.case4s;
		if (cardcmd.length == 7 && cardcmd[4] == 0)
			return apdutype.case2e;
		if (cardcmd.length == (7 + (cardcmd[5] & 0xff) * 256 + (cardcmd[6] & 0xff)) && cardcmd[4] == 0 && (cardcmd[5] != 0 || cardcmd[6] != 0))
			return apdutype.case3e;
		if (cardcmd.length == (9 + (cardcmd[5] & 0xff) * 256 + (cardcmd[6] & 0xff)) && cardcmd[4] == 0 && (cardcmd[5] != 0 || cardcmd[6] != 0))
			return apdutype.case4e;
		return null;
	}

	private void incrementAtIndex(byte[] array, int index) {
		if ((array[index] & 0xff) == 0xff) {
			array[index] = 0;
			if (index > 0)
				incrementAtIndex(array, index - 1);
		} else {
			array[index]++;
		}
	}
}
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_GM;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_GM_3DES_CBC_CBC;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_GM_AES_CBC_CMAC_128;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_GM_AES_CBC_CMAC_192;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_GM_AES_CBC_CMAC_256;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_IM;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_IM_3DES_CBC_CBC;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_IM_AES_CBC_CMAC_128;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_IM_AES_CBC_CMAC_192;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_DH_IM_AES_CBC_CMAC_256;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_CAM;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_CAM_AES_CBC_CMAC_128;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_CAM_AES_CBC_CMAC_192;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_CAM_AES_CBC_CMAC_256;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_GM;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_GM_3DES_CBC_CBC;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_GM_AES_CBC_CMAC_128;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_GM_AES_CBC_CMAC_192;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_GM_AES_CBC_CMAC_256;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_IM;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_IM_3DES_CBC_CBC;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_IM_AES_CBC_CMAC_128;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_IM_AES_CBC_CMAC_192;
import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_PACE_ECDH_IM_AES_CBC_CMAC_256;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECCurve.Fp;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import de.tsenger.animamea.AmCardHandler;
import de.tsenger.animamea.asn1.AmDHPublicKey;
import de.tsenger.animamea.asn1.AmECPublicKey;
import de.tsenger.animamea.asn1.BSIObjectIdentifiers;
import de.tsenger.animamea.asn1.CertificateHolderAuthorizationTemplate;
import de.tsenger.animamea.asn1.DiscretionaryData;
import de.tsenger.animamea.asn1.DomainParameter;
import de.tsenger.animamea.asn1.DynamicAuthenticationData;
import de.tsenger.animamea.asn1.PaceDomainParameterInfo;
import de.tsenger.animamea.asn1.PaceInfo;
import de.tsenger.animamea.crypto.AmAESCrypto;
import de.tsenger.animamea.crypto.AmCryptoException;
import de.tsenger.animamea.crypto.AmCryptoProvider;
import de.tsenger.animamea.crypto.AmDESCrypto;
import de.tsenger.animamea.crypto.KeyDerivationFunction;
import de.tsenger.animamea.iso7816.MSESetAT;
import de.tsenger.animamea.iso7816.SecureMessaging;
import de.tsenger.animamea.iso7816.SecureMessagingException;
import de.tsenger.animamea.tools.Converter;
import de.tsenger.animamea.tools.HexString;
		byte[] nonce_z = getNonce().getDataObject(0);

		logger.debug("encrypted nonce z: "+HexString.bufferToHex(nonce_z));
		byte[] nonce_s = decryptNonce(nonce_z);
		logger.debug("decrypted nonce s: "+HexString.bufferToHex(nonce_s));
		byte[] X1 = pace.getX1(nonce_s);

		// X1 zur Karte schicken und Y1 empfangen
		byte[] Y1 = mapNonce(X1).getDataObject(2);
		
		//Y1 ist PK_MapIc für id_PACE-CAM
		pk_mapic  = Y1.clone();

		byte[] X2 = pace.getX2(Y1);
		// X2 zur Karte schicken und Y2 empfangen.
		byte[] Y2 = performKeyAgreement(X2).getDataObject(4);
		
		// Y2 ist PK_Picc der für die id_TA benötigt wird.
		pk_picc = Y2.clone();

		byte[] S = pace.getSharedSecret_K(Y2);
		kenc = getKenc(S);
		kmac = getKmac(S);
		logger.debug("shared secret (K bzw S): "+HexString.bufferToHex(S));
		logger.debug("Kenc: "+HexString.bufferToHex(kenc));
		logger.debug("Kmac: "+HexString.bufferToHex(kmac));
		// Authentication Token T_PCD berechnen
		byte[] tpcd = calcAuthToken(kmac, Y2);

		// Authentication Token T_PCD zur Karte schicken und Authentication Token T_PICC empfangen
		DynamicAuthenticationData dad = performMutualAuthentication(tpcd);
		byte[] tpicc = dad.getDataObject(6);
		if (dad.getDataObject(7)!= null) {
			car = new String(dad.getDataObject(7));
			logger.info("CAR: "+car);
		}
		if (dad.getDataObject(8)!= null) {
			car2 = new String(dad.getDataObject(8));
			logger.info("CAR2: "+ car2);
		}
		if (dad.getDataObject(0x0A)!= null) {
			encCAdata = dad.getDataObject(0x0A);
			logger.info("Encrypted id_CA Data: "+HexString.bufferToHex(encCAdata));
			
		}

		// Authentication Token T_PICC' berechnen
		byte[] tpicc_strich = calcAuthToken(kmac, X2);
		logger.debug("tpicc': "+HexString.bufferToHex(tpicc_strich));

		// Prüfe ob T_PICC = T_PICC'
		if (!Arrays.areEqual(tpicc, tpicc_strich)) throw new PaceException("Authentication Tokens are different");
		
		return new SecureMessaging(crypto, kenc, kmac, new byte[crypto.getBlockSize()]);
	}
	


	/**
	 * Liefert den ephemeralen Public Key des Chips zurück. Dieser wird für Terminal
	 * Authentisierung nach V.2 benötigt.
	 * @return
	 */
	public PublicKey getPKpicc() {
		
		KeyFactory fact = null;
		PublicKey pubKey = null;
		KeySpec pubKeySpec = null;
		
		if (dp.getDPType().equals("ECDH")) {
			ECPoint q = Converter.byteArrayToECPoint(pk_picc, (Fp) dp.getECParameter().getCurve()).normalize();
			pubKeySpec = new ECPublicKeySpec(q, dp.getECParameter());	
			
		} else if (dp.getDPType().equals("DH")) {
			BigInteger y = new BigInteger(1, pk_picc);
			pubKeySpec = new DHPublicKeySpec(y, dp.getDHParameter().getP(), dp.getDHParameter().getG());
		}
		
		try {
			fact = KeyFactory.getInstance(dp.getDPType(), "BC");
			pubKey = fact.generatePublic(pubKeySpec);
		} catch (NoSuchAlgorithmException e) {
			logger.warn("Couldn't generate ephemeral public key.", e);
		} catch (NoSuchProviderException e) {
			logger.warn("Couldn't generate ephemeral public key.", e);
		} catch (InvalidKeySpecException e) {
			logger.warn("Couldn't generate ephemeral public key.", e);
		}
		
		return pubKey;
	}
	
	/** Liefert die nach PACE vom Chip zurückgelieferte Certificate Authority Reference
	 *  für die Terminal Authentisierung zurück. 
	 * @return aktuelle Certificate Authority Reference
	 */
	public String getCAR() {
		return car;
	}
	
	/** Liefert die nach PACE vom Chip zurückgelieferte alternative Certificate Authority Reference
	 *  für die Terminal Authentisierung zurück. 
	 * @return alternative Certificate Authority Reference
	 */
	public String getCAR2() {
		return car2;
	}
	
	/** Liefert die nach id_PACE-CAM vom Chip zurückgelieferte und entschlüsselte Chip Authentication Data zurück. 
	 * @return  Decrypted Chip Authentication Data
	 */
	
	public byte[] getCAData() {
		byte[] iv = new byte[16];
		for (int i=0;i<16;i++) iv[i] = (byte)0xFF;
		logger.info("IV: "+HexString.bufferToHex(iv));
		logger.info("Kenc: "+HexString.bufferToHex(kenc));
		crypto.init(kenc, iv);
		try {
			return crypto.decrypt(encCAdata);
		} catch (AmCryptoException e) {