use of es.gob.jmulticard.apdu.ResponseApdu 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()]);
}
use of es.gob.jmulticard.apdu.ResponseApdu in project jmulticard by ctt-gob-es.
the class Iso7816FourCard method readBinaryComplete.
/**
* Lee por completo el contenido binario del fichero actualmente seleccionado.
* @param len Longitud del fichero a leer.
* @return APDU de respuesta.
* @throws ApduConnectionException Si hay problemas en el envío de la APDU.
* @throws IOException Si hay problemas en el <i>buffer</i> de lectura.
*/
public byte[] readBinaryComplete(final int len) throws IOException {
int off = 0;
ResponseApdu readedResponse = null;
final ByteArrayOutputStream out = new ByteArrayOutputStream();
// Leemos en iteraciones de MAX_READ_CHUNK bytes
while (off < len) {
final byte msbOffset = (byte) (off >> 8);
final byte lsbOffset = (byte) (off & 0xFF);
final int left = len - off;
try {
if (left < MAX_READ_CHUNK) {
// Si es menor que el maximo que podemos leer por iteracion
readedResponse = readBinary(msbOffset, lsbOffset, (byte) left);
} else {
readedResponse = readBinary(msbOffset, lsbOffset, (byte) MAX_READ_CHUNK);
}
} catch (final Exception e) {
LOGGER.warning(// $NON-NLS-1$
"Se ha intentado una lectura fuera de los limites del fichero, se devolvera lo leido hasta ahora: " + e);
return out.toByteArray();
}
final boolean eofReached = EOF_REACHED.equals(readedResponse.getStatusWord());
if (!readedResponse.isOk() && !eofReached) {
// $NON-NLS-1$ //$NON-NLS-2$
throw new IOException("Error leyendo el binario (" + readedResponse.getStatusWord() + ")");
}
out.write(readedResponse.getData());
off += MAX_READ_CHUNK;
// Si hemos llegado al final no seguimos leyendo
if (eofReached) {
break;
}
}
return out.toByteArray();
}
use of es.gob.jmulticard.apdu.ResponseApdu in project jmulticard by ctt-gob-es.
the class Iso7816FourCard method selectFileByName.
/**
* Selecciona un fichero por nombre.
* @param name Nombre del fichero en hexadecimal
* @throws FileNotFoundException Si el fichero no existe
* @throws ApduConnectionException Si ocurre algún problema durante la selección
* @throws Iso7816FourCardException Si el fichero no se puede seleccionar por cualquier otra causa
*/
public void selectFileByName(final byte[] name) throws ApduConnectionException, FileNotFoundException, Iso7816FourCardException {
final CommandApdu selectCommand = new SelectDfByNameApduCommand(getCla(), name);
final ResponseApdu response = sendArbitraryApdu(selectCommand);
if (response.isOk()) {
return;
}
final StatusWord sw = response.getStatusWord();
if (sw.equals(new StatusWord((byte) 0x6A, (byte) 0x82))) {
throw new FileNotFoundException(name);
}
throw new Iso7816FourCardException(sw, selectCommand);
}
use of es.gob.jmulticard.apdu.ResponseApdu in project jmulticard by ctt-gob-es.
the class SmartCafePkcs15Applet method sign.
@Override
public byte[] sign(final byte[] data, final String algorithm, final PrivateKeyReference keyRef) throws CryptoCardException, PinException {
if (data == null) {
// $NON-NLS-1$
throw new CryptoCardException("Los datos a firmar no pueden ser nulos");
}
if (keyRef == null) {
// $NON-NLS-1$
throw new IllegalArgumentException("La clave privada no puede ser nula");
}
if (!(keyRef instanceof SmartCafePrivateKeyReference)) {
throw new IllegalArgumentException(// $NON-NLS-1$
"La clave proporcionada debe ser de tipo " + SmartCafePrivateKeyReference.class.getName() + // $NON-NLS-1$
", pero se ha recibido de tipo " + keyRef.getClass().getName());
}
final SmartCafePrivateKeyReference scPrivateKey = (SmartCafePrivateKeyReference) keyRef;
// Pedimos el PIN si no se ha pedido antes
if (!this.authenticated) {
try {
verifyPin(getInternalPasswordCallback());
this.authenticated = true;
} catch (final ApduConnectionException e1) {
// $NON-NLS-1$
throw new CryptoCardException("Error en la verificacion de PIN: " + e1, e1);
}
}
// Enviamos el MSE SET for Computation
ResponseApdu res = null;
try {
res = sendArbitraryApdu(new MseSetComputationApduCommand(// CLA
(byte) 0x01, new byte[] { (byte) scPrivateKey.getKeyOrdinal() }, // RSA
new byte[] { (byte) 0x02 }));
} catch (final ApduConnectionException e) {
throw new CryptoCardException(// $NON-NLS-1$ //$NON-NLS-2$
"Error estableciendo la clave y el algoritmo de firma (repuesta=" + res + "): " + e, // $NON-NLS-1$ //$NON-NLS-2$
e);
}
if (res == null || !res.isOk()) {
throw new CryptoCardException(// $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"No se ha podido establecer la clave y el algoritmo de firma" + (res != null ? " (repuesta=" + res + ")" : ""));
}
// Creamos el DigestInfo
final byte[] digestInfo;
try {
digestInfo = DigestInfo.encode(algorithm, data, this.cryptoHelper);
} catch (final IOException e) {
// $NON-NLS-1$
throw new CryptoCardException("Error en el calculo de la huella para firmar: " + e, e);
}
// Y lo enviamos a firmar
try {
res = sendArbitraryApdu(new PsoSignHashApduCommand((byte) 0x01, digestInfo));
} catch (final ApduConnectionException e) {
throw new CryptoCardException(// $NON-NLS-1$ //$NON-NLS-2$
"Error firmando (repuesta=" + res + "): " + e, // $NON-NLS-1$ //$NON-NLS-2$
e);
}
if (res == null || !res.isOk()) {
throw new CryptoCardException(// $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"No se ha podido firmar el DigestInfo" + (res != null ? " (repuesta=" + res + ")" : ""));
}
return res.getData();
}
use of es.gob.jmulticard.apdu.ResponseApdu in project jmulticard by ctt-gob-es.
the class SmartCafePkcs15Applet method getPinRetriesLeft.
private int getPinRetriesLeft() throws PinException {
final CommandApdu verifyCommandApdu = new RetriesLeftApduCommand();
final ResponseApdu verifyResponse;
try {
verifyResponse = getConnection().transmit(verifyCommandApdu);
} catch (final ApduConnectionException e) {
throw new PinException(// $NON-NLS-1$
"Error obteniendo el PIN del CallbackHandler: " + e, // $NON-NLS-1$
e);
}
if (verifyResponse.isOk() || verifyResponse.getBytes().length > 2) {
return verifyResponse.getBytes()[1];
}
throw new PinException(// $NON-NLS-1$
"Error comprobando los intentos restantes de PIN con respuesta: " + HexUtils.hexify(verifyResponse.getBytes(), true));
}
Aggregations