use of es.gob.jmulticard.de.tsenger.androsmex.crypto.AmCryptoProvider in project jmulticard by ctt-gob-es.
the class PaceChannelHelper method openPaceChannel.
/**
* Abre un canal PACE.
* @param cla Clase de APDU para los comandos de establecimiento de canal.
* @param pi Valor de inicialización del canal. Puede ser un CAN
* (<i>Card Access Number</i>) o una MRZ (<i>Machine Readable Zone</i>).
* @param conn Conexión hacia la tarjeta inteligente.
* @param cryptoHelper Clase para la realización de operaciones criptográficas auxiliares.
* @return SecureMessaging Objeto para el envío de mensajes seguros a través de canal PACE.
* @throws ApduConnectionException Si hay problemas de conexión con la tarjeta.
* @throws PaceException Si hay problemas en la apertura del canal.
*/
public static SecureMessaging openPaceChannel(final byte cla, final PaceInitializer pi, final ApduConnection conn, final CryptoHelper cryptoHelper) throws ApduConnectionException, PaceException {
if (conn == null) {
throw new IllegalArgumentException(// $NON-NLS-1$
"El canal de conexion no puede ser nulo");
}
if (pi == null) {
throw new IllegalArgumentException(// $NON-NLS-1$
"Es necesario proporcionar un inicializador para abrir canal PACE");
}
if (cryptoHelper == null) {
throw new IllegalArgumentException(// $NON-NLS-1$
"El CryptoHelper no puede ser nulo");
}
if (!conn.isOpen()) {
conn.open();
}
ResponseApdu res;
CommandApdu comm;
// 1.3.2 - Establecemos el algoritmo para PACE con el comando MSE Set:
comm = new MseSetPaceAlgorithmApduCommand(cla, MseSetPaceAlgorithmApduCommand.PaceAlgorithmOid.PACE_ECDH_GM_AES_CBC_CMAC128, pi.getPasswordType(), MseSetPaceAlgorithmApduCommand.PaceAlgorithmParam.BRAINPOOL_256_R1);
res = conn.transmit(comm);
if (!res.isOk()) {
throw new PaceException(res.getStatusWord(), comm, // $NON-NLS-1$
"Error estableciendo el algoritmo del protocolo PACE.");
}
// 1.3.3 - Primer comando General Autenticate - Get Nonce
comm = new GeneralAuthenticateApduCommand((byte) 0x10, new byte[] { (byte) 0x7C, (byte) 0x00 });
res = conn.transmit(comm);
if (!res.isOk()) {
throw new PaceException(res.getStatusWord(), comm, // $NON-NLS-1$
"Error solicitando el aleatorio de calculo PACE (Nonce)");
}
// Calcular nonce devuelto por la tarjeta que se empleara en los calculos
final byte[] nonce;
try {
nonce = new Tlv(new Tlv(res.getData()).getValue()).getValue();
} catch (final TlvException e) {
throw new PaceException(// $NON-NLS-1$ //$NON-NLS-2$
"El aleatorio de calculo PACE (Nonce) obtenido (" + HexUtils.hexify(res.getData(), true) + ") no sigue el formato esperado: " + e, // $NON-NLS-1$ //$NON-NLS-2$
e);
}
// Calcular sk = SHA-1( CAN || 00000003 );
// La clave son los 16 bytes MSB del hash
final byte[] sk = new byte[16];
try {
System.arraycopy(cryptoHelper.digest(CryptoHelper.DigestAlgorithm.SHA1, HexUtils.concatenateByteArrays(pi.getBytes(), CAN_MRZ_PADDING)), 0, sk, 0, 16);
} catch (final IOException e) {
throw new PaceException(// $NON-NLS-1$
"Error obteniendo el 'sk' a partir del CAN: " + e, // $NON-NLS-1$
e);
}
// Calcular secret = AES_Dec(nonce,sk);
final byte[] secret_nonce;
try {
secret_nonce = cryptoHelper.aesDecrypt(nonce, new byte[0], sk);
} catch (final Exception e) {
throw new PaceException(// $NON-NLS-1$
"Error descifranco el 'nonce': " + e, // $NON-NLS-1$
e);
}
// 1.3.4 - Segundo comando General Autenticate - Map Nonce
// Generamos un par de claves efimeras EC para el DH
// $NON-NLS-1$
final X9ECParameters ecdhParameters = TeleTrusTNamedCurves.getByName("brainpoolp256r1");
final ECPoint pointG = ecdhParameters.getG();
final Fp curve = (org.spongycastle.math.ec.ECCurve.Fp) ecdhParameters.getCurve();
// La privada del terminal se genera aleatoriamente (PrkIFDDH1)
// La publica de la tarjeta sera devuelta por ella misma al enviar nuesra publica (pukIFDDH1)
final Random rnd = new Random();
rnd.setSeed(rnd.nextLong());
final byte[] x1 = new byte[curve.getFieldSize() / 8];
rnd.nextBytes(x1);
final BigInteger PrkIFDDH1 = new BigInteger(1, x1);
// Enviamos nuestra clave publica (pukIFDDH1 = G*PrkIFDDH1)
final ECPoint pukIFDDH1 = pointG.multiply(PrkIFDDH1);
Tlv tlv = new Tlv(TAG_DYNAMIC_AUTHENTICATION_DATA, new Tlv(TAG_GEN_AUTH_2, pukIFDDH1.getEncoded(false)).getBytes());
// ... Y la enviamos a la tarjeta
comm = new GeneralAuthenticateApduCommand((byte) 0x10, tlv.getBytes());
res = conn.transmit(comm);
if (!res.isOk()) {
throw new PaceException(res.getStatusWord(), comm, // $NON-NLS-1$
"Error mapeando el aleatorio de calculo PACE (Nonce)");
}
// Se obtiene la clave publica de la tarjeta
final byte[] pukIccDh1;
try {
pukIccDh1 = unwrapEcKey(res.getData());
} catch (final Exception e) {
throw new PaceException(// $NON-NLS-1$
"Error obteniendo la clave efimera EC publica de la tarjeta: " + e, // $NON-NLS-1$
e);
}
// calcular blinding point H = PrkIFDDH1 * PukICCDH1
final ECPoint y1FromG = byteArrayToECPoint(pukIccDh1, curve);
// Calculamos el punto H secreto
final ECPoint SharedSecret_H = y1FromG.multiply(PrkIFDDH1);
// Se calcula el nuevo punto G' = nonce*G + H
final BigInteger ms = new BigInteger(1, secret_nonce);
final ECPoint g_temp = pointG.multiply(ms);
final ECPoint newPointG = g_temp.add(SharedSecret_H);
// 1.3.5 Tercer comando General Authenticate
// Se calcula la coordenada X de G' y generamos con la tarjeta un nuevo acuerdo de claves
// La privada del terminal se genera aleatoriamente (PrkIFDDH2)
// La publica de la tarjeta sera devuelta por ella misma al enviar nuesra publica (pukIFDDH2)
final byte[] x2 = new byte[curve.getFieldSize() / 8];
rnd.setSeed(rnd.nextLong());
rnd.nextBytes(x2);
final BigInteger PrkIFDDH2 = new BigInteger(1, x2);
// Enviamos nuestra clave publica (pukIFDDH2 = G'*PrkIFDDH2)
final ECPoint pukIFDDH2 = newPointG.multiply(PrkIFDDH2);
// ... La metemos en un TLV de autenticacion ...
tlv = new Tlv(TAG_DYNAMIC_AUTHENTICATION_DATA, new Tlv(TAG_GEN_AUTH_3, pukIFDDH2.getEncoded(false)).getBytes());
comm = new GeneralAuthenticateApduCommand((byte) 0x10, tlv.getBytes());
res = conn.transmit(comm);
// Se obtiene la clave publica de la tarjeta (pukIccDh2) que es la coordenada Y del nuevo Punto G'
final byte[] pukIccDh2;
try {
pukIccDh2 = unwrapEcKey(res.getData());
} catch (final Exception e) {
throw new PaceException(// $NON-NLS-1$
"Error obteniendo la clave efimera EC publica de la tarjeta: " + e, // $NON-NLS-1$
e);
}
final ECPoint y2FromNewG = byteArrayToECPoint(pukIccDh2, curve);
// Se calcula el secreto k = PukICCDH2 * PrkIFDDH2
final ECPoint.Fp SharedSecret_K = (org.spongycastle.math.ec.ECPoint.Fp) y2FromNewG.multiply(PrkIFDDH2);
final byte[] secretK = bigIntToByteArray(SharedSecret_K.normalize().getXCoord().toBigInteger());
// 1.3.6 Cuarto comando General Authenticate
// Se validan las claves de sesion generadas en el paso anterior,
// por medio de un MAC que calcula el terminal y comprueba la tarjeta,
// la cual devolvera un segundo MAC.
// Calcular kenc = SHA-1( k || 00000001 );
final byte[] kenc = new byte[16];
try {
System.arraycopy(cryptoHelper.digest(CryptoHelper.DigestAlgorithm.SHA1, HexUtils.concatenateByteArrays(secretK, KENC_PADDING)), 0, kenc, 0, 16);
} catch (final IOException e) {
throw new PaceException(// $NON-NLS-1$
"Error obteniendo el 'kenc' a partir del CAN: " + e, // $NON-NLS-1$
e);
}
// Calcular kmac = SHA-1( k || 00000002 );
final byte[] kmac = new byte[16];
try {
System.arraycopy(cryptoHelper.digest(CryptoHelper.DigestAlgorithm.SHA1, HexUtils.concatenateByteArrays(secretK, KMAC_PADDING)), 0, kmac, 0, 16);
} catch (final IOException e) {
throw new PaceException(// $NON-NLS-1$
"Error obteniendo el 'kmac' a partir del CAN: " + e, // $NON-NLS-1$
e);
}
// Elimina el byte '04' del inicio que es el indicador de punto descomprimido
final byte[] pukIccDh2Descompressed = new byte[pukIccDh2.length - 1];
System.arraycopy(pukIccDh2, 1, pukIccDh2Descompressed, 0, pukIccDh2.length - 1);
// Se calcula el Mac del terminal: data = '7f494F06' + oid + '864104' + PukICCDH2;
final byte[] data = HexUtils.concatenateByteArrays(MAC_PADDING, HexUtils.concatenateByteArrays(MseSetPaceAlgorithmApduCommand.PaceAlgorithmOid.PACE_ECDH_GM_AES_CBC_CMAC128.getBytes(), HexUtils.concatenateByteArrays(MAC2_PADDING, pukIccDh2Descompressed)));
byte[] mac8bytes;
try {
mac8bytes = cryptoHelper.doAesCmac(data, kmac);
} catch (final Exception e) {
throw new PaceException(// $NON-NLS-1$
"Error descifrando el 'nonce': " + e, // $NON-NLS-1$
e);
}
// ... La metemos en un TLV de autenticacion ...
tlv = new Tlv(TAG_DYNAMIC_AUTHENTICATION_DATA, new Tlv(TAG_GEN_AUTH_4, mac8bytes).getBytes());
// Se envia el comando General Authenticate y se recupera el MAC devuelto por la tarjeta.
comm = new GeneralAuthenticateApduCommand((byte) 0x00, tlv.getBytes());
res = conn.transmit(comm);
// Se obtiene un MAC con respuesta 90-00 indicando que se ha establecido el canal correctamente
if (!res.isOk()) {
throw new PaceException(res.getStatusWord(), comm, // $NON-NLS-1$
"Error estableciendo el algoritmo del protocolo PACE.");
}
// Se inicializa el contador de secuencia a ceros
final byte[] ssc = new byte[16];
Arrays.fill(ssc, (byte) 0);
// $NON-NLS-1$
LOGGER.info("Canal Pace abierto");
LOGGER.info(// $NON-NLS-1$
"\nKenc: " + HexUtils.hexify(kenc, true) + "Kmac: " + // $NON-NLS-1$
HexUtils.hexify(kmac, true) + "Ssc: " + // $NON-NLS-1$
HexUtils.hexify(ssc, true));
final AmCryptoProvider crypto = new AmAESCrypto();
return new SecureMessaging(crypto, kenc, kmac, new byte[crypto.getBlockSize()]);
}
Aggregations