Java tutorial
/** * Copyright 2011, Tobias Senger * * This file is part of animamea. * * Animamea is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Animamea is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with animamea. If not, see <http://www.gnu.org/licenses/>. */ package de.tsenger.animamea.ca; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_DH_3DES_CBC_CBC; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_DH_AES_CBC_CMAC_128; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_DH_AES_CBC_CMAC_192; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_DH_AES_CBC_CMAC_256; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_ECDH_3DES_CBC_CBC; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_ECDH_AES_CBC_CMAC_128; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_ECDH_AES_CBC_CMAC_192; import static de.tsenger.animamea.asn1.BSIObjectIdentifiers.id_CA_ECDH_AES_CBC_CMAC_256; import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.jce.provider.JCEDHPublicKey; 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.ChipAuthenticationInfo; import de.tsenger.animamea.asn1.ChipAuthenticationPublicKeyInfo; import de.tsenger.animamea.asn1.DomainParameter; import de.tsenger.animamea.asn1.DynamicAuthenticationData; import de.tsenger.animamea.crypto.AmAESCrypto; 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.HexString; /** * @author Tobias Senger (tobias@t-senger.de) * */ public class CAOperator { private AmCardHandler ch = null; private PrivateKey ephSKPCD = null; private PublicKey ephPKPCD = null; private byte[] caPK = null; private DomainParameter dp = null; private int caPKref; private String protocol = null; private ChipAuthentication ca = null; private int keyLength; private AmCryptoProvider crypto = null; /** * Constructor * @param ch CardHandler */ public CAOperator(AmCardHandler ch) { this.ch = ch; } public void initialize(ChipAuthenticationInfo caInfo, ChipAuthenticationPublicKeyInfo caPKInfo, KeyPair ephPCDKeyPair) throws CAException { this.protocol = caInfo.getProtocolOID().toString(); this.caPK = caPKInfo.getPublicKey().getPublicKey(); this.caPKref = caInfo.getKeyId(); if (caPKref != caPKInfo.getKeyId()) throw new CAException( "Key Identifier in ChipAuthenticationInfo and ChipAuthenticationPublicKeyInfo doesn't match"); this.dp = new DomainParameter(caPKInfo.getPublicKey().getAlgorithm()); if (dp.getDPType().equals("ECDH")) { ca = new ChipAuthenticationECDH(dp.getECParameter()); } else if (dp.getDPType().equals("DH")) { ca = new ChipAuthenticationDH(dp.getDHParameter()); } this.ephSKPCD = ephPCDKeyPair.getPrivate(); this.ephPKPCD = ephPCDKeyPair.getPublic(); getCryptoInformation(caInfo); } public SecureMessaging performCA() throws SecureMessagingException, CardException, CAException { //send MSE:Set AT MSESetAT mse = new MSESetAT(); mse.setAT(MSESetAT.setAT_CA); mse.setProtocol(protocol); mse.setPrivateKeyReference(caPKref); ch.transceive(mse.getCommandAPDU()); // General Authenticate DynamicAuthenticationData dad = sendGA(); //TODO Rckgabe der Karte prfen (z.B. SW != 9000) //Schlssel fr Secure Messaging berechnen byte[] rnd_picc = dad.getDataObject(1); byte[] K = ca.getSharedSecret_K(ephSKPCD, caPK); byte[] kenc = null; byte[] kmac = null; switch (keyLength) { case 112: kenc = new KeyDerivationFunction(K, rnd_picc, 1).getDESedeKey(); kmac = new KeyDerivationFunction(K, rnd_picc, 2).getDESedeKey(); break; case 128: kenc = new KeyDerivationFunction(K, rnd_picc, 1).getAES128Key(); kmac = new KeyDerivationFunction(K, rnd_picc, 2).getAES128Key(); break; case 192: kenc = new KeyDerivationFunction(K, rnd_picc, 1).getAES192Key(); kmac = new KeyDerivationFunction(K, rnd_picc, 2).getAES192Key(); break; case 256: kenc = new KeyDerivationFunction(K, rnd_picc, 1).getAES256Key(); kmac = new KeyDerivationFunction(K, rnd_picc, 2).getAES256Key(); break; } //Authentication Token vergleichen byte[] tpcd = calcToken(kmac, ephPKPCD); if (!Arrays.areEqual(tpcd, dad.getDataObject(2))) throw new CAException("Authentication Tokens are different. Cards Token:\n" + HexString.bufferToHex(dad.getDataObject(2)) + "calculated Token:\n" + HexString.bufferToHex(tpcd)); return new SecureMessaging(crypto, kenc, kmac, new byte[crypto.getBlockSize()]); } private byte[] calcToken(byte[] kmac, PublicKey data) { byte[] tpcd = null; if (ca instanceof ChipAuthenticationECDH) { ECPoint point = ((ECPublicKey) data).getQ(); AmECPublicKey pk = new AmECPublicKey(protocol, point); tpcd = crypto.getMAC(kmac, pk.getEncoded()); } else if (ca instanceof ChipAuthenticationDH) { BigInteger y = ((JCEDHPublicKey) data).getY(); AmDHPublicKey pk = new AmDHPublicKey(protocol, y); tpcd = crypto.getMAC(kmac, pk.getEncoded()); } return tpcd; } private DynamicAuthenticationData sendGA() throws SecureMessagingException, CardException { DynamicAuthenticationData dad80 = new DynamicAuthenticationData(); dad80.addDataObject(0, ((ECPublicKey) ephPKPCD).getQ().getEncoded()); byte[] dadBytes = null; try { dadBytes = dad80.getEncoded(ASN1Encoding.DER); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //TODO Length Expected steht hier auf 0xFF weil CommandAPDU den Wert 0x00 nicht bercksichtigt. ResponseAPDU resp = ch.transceive(new CommandAPDU(0x00, 0x86, 00, 00, dadBytes, 0xFF)); DynamicAuthenticationData dad = new DynamicAuthenticationData(resp.getData()); return dad; } /** * Ermittelt anhand der ProtokollOID den Algorithmus und die Schlssellnge * fr Chip Authentication */ private void getCryptoInformation(ChipAuthenticationInfo cai) { String protocolOIDString = cai.getProtocolOID(); if (protocolOIDString.equals(id_CA_DH_3DES_CBC_CBC.toString()) || protocolOIDString.equals(id_CA_ECDH_3DES_CBC_CBC.toString())) { keyLength = 112; crypto = new AmDESCrypto(); } else if (protocolOIDString.equals(id_CA_DH_AES_CBC_CMAC_128.toString()) || protocolOIDString.equals(id_CA_ECDH_AES_CBC_CMAC_128.toString())) { keyLength = 128; crypto = new AmAESCrypto(); } else if (protocolOIDString.equals(id_CA_DH_AES_CBC_CMAC_192.toString()) || protocolOIDString.equals(id_CA_ECDH_AES_CBC_CMAC_192.toString())) { keyLength = 192; crypto = new AmAESCrypto(); } else if (protocolOIDString.equals(id_CA_DH_AES_CBC_CMAC_256.toString()) || protocolOIDString.equals(id_CA_ECDH_AES_CBC_CMAC_256.toString())) { keyLength = 256; crypto = new AmAESCrypto(); } } }