Я разрабатываю смарт-карту, которая связывается через промежуточное ПО с сервером.Сервер должен аутентифицировать себя на смарт-карте.Частью этого процесса аутентификации является вызов, который зашифрован с помощью ключа сеанса.Сервер должен расшифровать вызов с помощью сеансового ключа.Это происходит со следующими параметрами шифра:
Алгоритм: AES Mode: CBC Padding: noPadding
Код для шифрования на карте:
public class Card extends Applet {
private final static byte[] PUBLIC_KEY_EXPONENT_CA = new byte[] {(byte) 0x1, 0x00, 0x01};
private final static byte[] PUBLIC_KEY_MODULUS_CA = new byte[] { (byte) 0xC0, 0x16, 0x35, 0x2C, 0x2F, 0x0E, 0x6C, 0x6B, (byte) 0x96, (byte) 0x9F, 0x53, 0x0B, 0x00, (byte) 0xE9, 0x05, 0x71, (byte) 0xCB, 0x39, (byte) 0xD0, 0x23, (byte) 0xF0, (byte) 0x80, 0x45, 0x75, 0x00, (byte) 0xCD, (byte) 0x80, (byte) 0xC7, 0x4B, 0x3A, 0x1B, 0x61, (byte) 0x86, (byte) 0xF4, 0x44, (byte) 0xCD, 0x21, 0x63, (byte) 0xCB, 0x44, 0x63, (byte) 0x9D, (byte) 0x97, 0x4F, 0x40, (byte) 0xA4, 0x1E, 0x01, 0x16, 0x39, 0x1D, (byte) 0xE5, 0x67, (byte) 0xBE, 0x01, (byte) 0xC8, (byte) 0x82, 0x52, 0x0B, 0x13, 0x71, (byte) 0xF5, 0x38, 0x21 };
private final RSAPublicKey publicKeyCA;
private final static byte[] PUBLIC_KEY_EXPONENT_SP = new byte[3];
private final static byte[] PUBLIC_KEY_MODULUS_SP = new byte[64];
private final RSAPublicKey publicKeySP;
private final static byte[] KEY_SESSION = new byte[32];
private final static byte[] CHALLENGE_SESSION = new byte[32];
private static final short LENGTH_SIGNATURE = 64;
private static final short LENGTH_TIME = 8;
private final byte[] time = new byte[LENGTH_TIME];
private final byte[] lastValidationTime = new byte[LENGTH_TIME];
private final byte[] differenceTime = new byte[LENGTH_TIME];
private final byte[] signatureBytes = new byte[LENGTH_SIGNATURE];
private final byte[] hash = new byte[20];
private final byte[] storage = new byte[331];
private final RandomData random;
private final Signature signature;
private final MessageDigest digest;
private final Cipher cipherRSA;
private final Cipher cipherAES;
private Card() {
publicKeySP = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_512, false);
signature = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
digest = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
cipherRSA = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
cipherAES = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
random = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM) ;
}
public void process(APDU apdu) throws ISOException {
byte[] buffer = apdu.getBuffer();
...
byte instruction = buffer[ISO7816.OFFSET_INS];
switch(instruction){
...
case INS_AUTHENTICATE_SP:
authenticateSP(apdu);
break;
default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
private void authenticateSP(APDU apdu) {
loadSPCertificate();
if (!publicKeySP.isInitialized()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
verifySPCertificate();
clear(KEY_SESSION);
clear(CHALLENGE_SESSION);
AESKey sessionKeySP = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_128, true);
random.generateData(KEY_SESSION, (short) 0, (short) KEY_SESSION.length);
sessionKeySP.setKey(KEY_SESSION, (short) 0);
byte[] sessionKeySPBytesEncrypted = new byte[64];
cipherRSA.init(publicKeySP, Cipher.MODE_ENCRYPT);
cipherRSA.doFinal(KEY_SESSION, (short) 0, (short) KEY_SESSION.length, sessionKeySPBytesEncrypted, (short) 0);
random.generateData(CHALLENGE_SESSION, (short) 0, (short) 32);
byte[] messageBytes = new byte[128];
byte[] messageBytesEncrypted = new byte[128];
Util.arrayCopy(CHALLENGE_SESSION, (short) 0, messageBytes, (short) 0, (short) 32);
Util.arrayCopy(storage, (short) 0, messageBytes, (short) 32, (short) 64);
Util.arrayFillNonAtomic(messageBytes, (short) 96, (short) 32, (byte) -1);
cipherAES.init(sessionKeySP, Cipher.MODE_ENCRYPT, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, (short) 0, (short) 16);
cipherAES.doFinal(messageBytes, (short) 0, (short) messageBytes.length, messageBytesEncrypted, (short) 0);
apdu.setOutgoing();
apdu.setOutgoingLength((short) (sessionKeySPBytesEncrypted.length + messageBytesEncrypted.length));
apdu.sendBytesLong(sessionKeySPBytesEncrypted, (short) 0, (short) sessionKeySPBytesEncrypted.length);
apdu.sendBytesLong(messageBytesEncrypted, (short) 0, (short) messageBytesEncrypted.length);
}
private void verifySPCertificate() {
signature.init(publicKeyCA, Signature.MODE_VERIFY);
// The signature is used on the hash of the certificate without the signature bytes
Util.arrayCopy(storage, (short) 267, signatureBytes, (short) 0, (short) LENGTH_SIGNATURE);
digest.doFinal(storage, (short) 0, (short) 267, hash, (short) 0);
if (!signature.verify(hash, (short) 0, (short) hash.length, signatureBytes, (short) 0, (short) signatureBytes.length)) {
ISOException.throwIt(SW_VERIFY_CERTIFICATE_ERROR);
}
Util.arrayCopy(storage, (short) 259, time, (short) 0, (short) time.length);
byte result = BigIntNumber.compare(time, (byte) 259, lastValidationTime, (byte) 0, (byte) LENGTH_TIME);
if (result < 0) {
ISOException.throwIt(SW_VERIFY_CERTIFICATE_ERROR);
}
}
private void loadSPCertificate() {
Util.arrayCopy(storage, (short) 128, PUBLIC_KEY_EXPONENT_SP, (short) 0, (short) PUBLIC_KEY_EXPONENT_SP.length);
Util.arrayCopy(storage, (short) 131, PUBLIC_KEY_MODULUS_SP, (short) 0, (short) PUBLIC_KEY_MODULUS_SP.length);
publicKeySP.setExponent(PUBLIC_KEY_EXPONENT_SP, (short) 0, (short) PUBLIC_KEY_EXPONENT_SP.length);
publicKeySP.setModulus(PUBLIC_KEY_MODULUS_SP, (short) 0, (short) PUBLIC_KEY_MODULUS_SP.length);
}
}
sessionKeySP: [89, 93, -96, 94, 97, -115, -91, -90, 100, -17, 106, -109, 18, 114, -11, 3, -53, 116, 64, 100, -96, 38, 57, -21, 52, -111, 32, -97, 58, 39, 25, -128]
messageBytes: [32, -52, -49, -20, -85, 41, 125, 116, 45, -53, 53, 20, -53, -84, -128, -67, -114, -68, 66, -96, 67, 78, 61, 115, 112, 84, 97, 120, 79, 110, 87, 101, 98, 44, 32, 68, 67, 61, 101, 71, 111, 118, 101, 114, 110, 109, 101, 110, 116, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
messageBytesEncrypted: [-15, -114, 51, 112, 32, -108, 72, 108, 4, -77, -126, 78, 89, -34, 5, 53, 51, -84, 99, 28, -16, -3, 50, 50, -31, 108, -98, -18, -113, -126, 18, 18, 72, 7, 49, -76, -7, 111, -33, 47, 18, -94, 82, -49, -75, 60, -113, -49, 80, 101, -71, -43, 29, 52, -33, 15, -71, 89, 7, 6, -15, 30, -69, 103, -14, 91, -116, -94, -99, -120, -110, 42, -76, 10, -17, -58, -77, 80, 57, 43, -128, -72, 104, -69, -9, -103, -123, -125, -98, 25, -116, 96, 13, -38, -78, 87, -18, 103, 0, 15, 0, -63, -19, 86, 31, -21, 41, -127, -127, 58, -104, 89, 41, 18, -70, -86, 98, -33, 58, 80, -12, -102, 7, -106, -14, -18, -69, 34]
Код для расшифровкина сервере:
private byte[] verifyAuthenticationServiceProvider(byte[] encryptedData) throws Exception {
byte[] sessionKeyBytesEncrypted = new byte[64];
System.arraycopy(encryptedData, 0, sessionKeyBytesEncrypted, 0, 64);
byte[] messageBytesEncrypted = new byte[128];
System.arraycopy(encryptedData, 64, messageBytesEncrypted, 0, 128);
Cipher cipherRSA = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipherRSA.init(Cipher.DECRYPT_MODE, privateKey);
byte[] sessionKeyBytes = cipherRSA.doFinal(sessionKeyBytesEncrypted);
SecretKey sessionKey = new SecretKeySpec(sessionKeyBytes, 0, sessionKeyBytes.length, "AES");
Cipher cipherAES = Cipher.getInstance("AES/CBC/NoPadding");
IvParameterSpec ivspec = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
cipherAES.init(Cipher.DECRYPT_MODE, sessionKey, ivspec);
byte[] messageBytes = cipherAES.doFinal(messageBytesEncrypted);
byte[] challengeBytes = new byte[32];
byte[] subjectBytes = new byte[64];
System.arraycopy(messageBytes, 0, challengeBytes, 0, 32);
System.arraycopy(messageBytes, 32, subjectBytes, 0, 64);
...
}
sessionKey: [89, 93, -96, 94, 97, -115, -91, -90, 100, -17, 106, -109, 18, 114, -11, 3, -53, 116, 64, 100, -96, 38, 57, -21, 52, -111, 32, -97, 58, 39, 25, -128]
messageBytesEncrypted: [-15, -114, 51, 112, 32, -108, 72, 108, 4, -77, -126, 78, 89, -34, 5, 53, 51, -84, 99, 28, -16, -3, 50, 50, -31, 108, -98, -18, -113, -126, 18, 18, 72, 7, 49, -76, -7, 111, -33, 47, 18, -94, 82, -49, -75, 60, -113, -49, 80, 101, -71, -43, 29, 52, -33, 15, -71, 89, 7, 6, -15, 30, -69, 103, -14, 91, -116, -94, -99, -120, -110, 42, -76, 10, -17, -58, -77, 80, 57, 43, -128, -72, 104, -69, -9, -103, -123, -125, -98, 25, -116, 96, 13, -38, -78, 87, -18, 103, 0, 15, 0, -63, -19, 86, 31, -21, 41, -127, -127, 58, -104, 89, 41, 18, -70, -86, 98, -33, 58, 80, -12, -102, 7, -106, -14, -18, -69, 34]
messageBytes: [-74, -125, 17, -98, 106, -83, -100, 29, 89, 70, -87, -122, -104, 84, 36, -34, 87, -111, 10, 13, -12, -112, -48, -90, 127, 75, 95, 64, 11, -96, 25, 26, 92, -111, 10, 6, 93, 36, -95, 127, -50, 65, 15, -111, -82, 48, 2, 94, 8, -6, -102, -96, -43, -110, -57, -26, -79, -42, -57, 121, -110, -74, -108, 17, 44, -28, -43, -96, 79, 60, -91, 23, -67, -76, 10, -1, -57, -11, 10, 114, -74, 107, -99, -83, 38, 109, 99, -13, 44, 102, 112, 74, -13, -84, 60, -101, -19, -78, -50, 29, 50, 105, -10, -103, -107, -47, -60, 78, 61, -35, -12, 114, -21, -34, -110, -103, -91, -70, -73, 0, 118, 45, 113, 40, 99, -76, -8, 126]
К сожалению, расшифрованные сообщенияBytes не совпадают с теми, которые были отправлены на карту.Почему-то дешифрование (на сервере) отличается от шифрования (на карте), но я не могу понять, почему и как.Моя конфигурация шифра на карте такая же, как и на сервере.
Обратите внимание, что я проверил правильность используемого сеансового ключа и IvParameters как на карте, так и на сервере.
Также обратите внимание, что я осведомлен о навязанных рисках безопасности, связанных с этим криптографическим кодом.
Обновление 1
Теперь, включая ключ сеанса в байтах.Кроме того, я обновил размер задачи до 32 байт (потому что после этого мне нужно снова зашифровать его с помощью AES).
Обновление 2
Теперь, включая весь код накарта для метода authenticateSP.