Я пытаюсь реализовать этот метод расшифровки с Java на С#: https://github.com/ecsec/open-ecard/blob/f66ae48e7bbb2bb27a524e12d3febabf162c17c7/ifd/ifd-protocols/pace/src/main/java/org/openecard/ifd/protocol/pace/SecureMessaging.java#L198C3- L198C90
/**
* Decrypt the APDU.
*
* @param response the response
* @param secureMessagingSSC the secure messaging ssc
* @return the byte[]
* @throws Exception the exception
*/
private byte[] decrypt(byte[] response, byte[] secureMessagingSSC) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(response);
ByteArrayOutputStream baos = new ByteArrayOutputStream(response.length - 10);
// Status bytes of the response APDU. MUST be 2 bytes.
byte[] statusBytes = new byte[2];
// Padding-content indicator followed by cryptogram 0x87.
byte[] dataObject = null;
// Cryptographic checksum 0x8E. MUST be 8 bytes.
byte[] macObject = new byte[8];
/*
* Read APDU structure
* Case 1: DO99|DO8E|SW1SW2
* Case 2: DO87|DO99|DO8E|SW1SW2
* Case 3: DO99|DO8E|SW1SW2
* Case 4: DO87|DO99|DO8E|SW1SW2
*/
byte tag = (byte) bais.read();
// Read data object (OPTIONAL)
if (tag == (byte) 0x87) {
int size = bais.read();
if (size > 0x80) {
byte[] sizeBytes = new byte[size & 0x0F];
bais.read(sizeBytes, 0, sizeBytes.length);
size = new BigInteger(1, sizeBytes).intValue();
}
bais.skip(1); // Skip encryption header
dataObject = new byte[size - 1];
bais.read(dataObject, 0, dataObject.length);
tag = (byte) bais.read();
}
// Read processing status (REQUIRED)
if (tag == (byte) 0x99) {
if (bais.read() == (byte) 0x02) {
bais.read(statusBytes, 0, 2);
tag = (byte) bais.read();
}
} else {
throw new IOException("Malformed Secure Messaging APDU");
}
// Read MAC (REQUIRED)
if (tag == (byte) 0x8E) {
if (bais.read() == (byte) 0x08) {
bais.read(macObject, 0, 8);
}
} else {
throw new IOException("Malformed Secure Messaging APDU");
}
// Only 2 bytes status should remain
if (bais.available() != 2) {
throw new IOException("Malformed Secure Messaging APDU");
}
// Calculate MAC for verification
CMac cmac = getCMAC(secureMessagingSSC);
byte[] mac = new byte[16];
synchronized (cmac) {
ByteArrayOutputStream macData = new ByteArrayOutputStream();
// Write padding-content
if (dataObject != null) {
TLV paddedDataObject = new TLV();
paddedDataObject.setTagNumWithClass((byte) 0x87);
paddedDataObject.setValue(ByteUtils.concatenate((byte) 0x01, dataObject));
macData.write(paddedDataObject.toBER());
}
// Write status bytes
TLV statusBytesObject = new TLV();
statusBytesObject.setTagNumWithClass((byte) 0x99);
statusBytesObject.setValue(statusBytes);
macData.write(statusBytesObject.toBER());
byte[] paddedData = pad(macData.toByteArray(), 16);
cmac.update(paddedData, 0, paddedData.length);
cmac.doFinal(mac, 0);
mac = ByteUtils.copy(mac, 0, 8);
}
// Verify MAC
if (!ByteUtils.compare(mac, macObject)) {
throw new GeneralSecurityException("Secure Messaging MAC verification failed");
}
// Decrypt data
if (dataObject != null) {
Cipher c = getCipher(secureMessagingSSC, Cipher.DECRYPT_MODE);
byte[] data_decrypted = c.doFinal(dataObject);
baos.write(unpad(data_decrypted));
}
// Add status code
baos.write(statusBytes);
return baos.toByteArray();
}
Вот мой метод, я не знаю, как реализовать прокомментированную часть:
public byte[] decrypt(byte[] response, byte[] secureMessagingSSC)
{
using (MemoryStream bais = new MemoryStream(response))
using (MemoryStream baos = new MemoryStream(response.Length - 10))
{
byte[] statusBytes = new byte[2];
byte[] dataObject = null;
byte[] macObject = new byte[8];
byte tag = (byte)bais.ReadByte();
if (tag == 0x87)
{
int size = bais.ReadByte();
if (size > 0x80)
{
byte[] sizeBytes = new byte[size & 0x0F];
bais.Read(sizeBytes, 0, sizeBytes.Length);
size = new BigInteger(1, sizeBytes).IntValue;
}
bais.Seek(1, SeekOrigin.Current); // Skip encryption header
dataObject = new byte[size - 1];
bais.Read(dataObject, 0, dataObject.Length);
tag = (byte)bais.ReadByte();
}
if (tag == 0x99)
{
if (bais.ReadByte() == 0x02)
{
bais.Read(statusBytes, 0, 2);
tag = (byte)bais.ReadByte();
}
}
else
{
throw new IOException("Malformed Secure Messaging APDU");
}
if (tag == 0x8E)
{
if (bais.ReadByte() == 0x08)
{
bais.Read(macObject, 0, 8);
}
}
else
{
throw new IOException("Malformed Secure Messaging APDU");
}
// Only 2 bytes status should remain
if (bais.Length - bais.Position != 2)
{
throw new IOException("Malformed Secure Messaging APDU");
}
// Calculate MAC for verification
CMac cmac = getCMAC(secureMessagingSSC);
byte[] mac = new byte[16];
lock (cmac)
{
MemoryStream macData = new MemoryStream();
// Write padding-content
if (dataObject != null)
{
TLV paddedDataObject = new TLV();
paddedDataObject.SetTagNumWithClass(0x87);
paddedDataObject.SetValue(ByteUtils.Concatenate((byte)0x01, dataObject));
macData.Write(paddedDataObject.ToBER(), 0, paddedDataObject.ToBER().Length);
}
// Write status bytes
TLV statusBytesObject = new TLV();
statusBytesObject.SetTagNumWithClass(0x99);
statusBytesObject.SetValue(statusBytes);
macData.Write(statusBytesObject.ToBER(), 0, statusBytesObject.ToBER().Length);
byte[] paddedData = pad(macData.ToArray(), 16);
cmac.BlockUpdate(paddedData, 0, paddedData.Length);
cmac.DoFinal(mac, 0);
mac = ByteUtils.Copy(mac, 0, 8);
}
// Verify MAC
if (!ByteUtils.Compare(mac, macObject))
{
throw new GeneralSecurityException("Secure Messaging MAC verification failed");
}
baos.Write(statusBytes, 0, statusBytes.Length);
return baos.ToArray();
}
}
У меня всегда 900, когда я расшифровываю зашифрованное значение: Вы можете протестировать код здесь: https://dotnetfiddle.net/33V9Bl
Проблема в том, что проверка Mac всегда не удалась, и я не знаю, как ее обновить. у меня такое: проверка MAC безопасного обмена сообщениями не удалась. Я реализовал недостающее.
Проблема в вашей реализации. Это ошибка.
В коде есть две проблемы: одна с аутентификацией, другая с расшифровкой:
Проблема аутентификации:
Аутентификация не удалась, поскольку ваша реализация класса TLV
и/или связанных с ним классов ошибочна. Точнее, ошибка заключается в портировании перечисления TagClass
. Хотя типы перечислений могут содержать статические методы в Java, в C# это невозможно (см. здесь). При портировании статический метод TagClass.getTagClass()
просто не был портирован.
Возможное решение — реализовать отсутствующий метод в классе Tag
, например:
public static TagClass GetTagClass(byte octet)
{
byte classByte = (byte)((octet >> 6) & 0x03);
switch (classByte)
{
case 0: return TagClass.UNIVERSAL;
case 1: return TagClass.APPLICATION;
case 2: return TagClass.CONTEXT;
case 3: return TagClass.PRIVATE;
default: throw new GeneralSecurityException("Unknown tag class: " + classByte);
}
}
Кроме того, в методе Tag.FromBer()
замените строку:
TagClass tagClass = (TagClass)data[0];
с линией:
TagClass tagClass = GetTagClass(data[0]);
При этом изменении аутентификация проходит успешно.
Проблема с расшифровкой:
Недостающее дешифрование можно реализовать компактно, например. с Aes#DecryptCbc()
:
using System.Security.Cryptography;
...
using (Aes aes = Aes.Create())
{
aes.Key = keyENC;
byte[] decyptedPadded = aes.DecryptCbc(dataObject, getCipherIV(secureMessagingSSC), PaddingMode.None);
byte[] decrypted = Unpad(decyptedPadded);
baos.Write(decrypted);
}
Благодаря этому изменению зашифрованный текст успешно расшифровывается.
Редактировать:
Если, как отмечено в комментарии, DecryptCbc()
недоступен в используемой версии .NET, его можно расшифровать следующим образом:
using (Aes aes = Aes.Create())
{
aes.Key = keyENC;
aes.IV = getCipherIV(secureMessagingSSC);
aes.Padding = PaddingMode.None;
using (ICryptoTransform decryptor = aes.CreateDecryptor())
{
byte[] decryptedPadded = decryptor.TransformFinalBlock(dataObject, 0, dataObject.Length);
byte[] decrypted = Unpad(decryptedPadded);
baos.Write(decrypted, 0, decrypted.Length);
}
}
У меня ошибка aes.DecrptCbc, я пытаюсь это исправить dotnetfiddle.net/wQMngy но проверка Mac не удалась
@Bouls - В моем первом комментарии есть код, который доказал свою эффективность. Почему бы вам не использовать его?
Я взял тот же код, вы можете проверить его по ссылке, предоставленной ранее, SDK использует другую версию .Net, у меня есть ошибка с DecryptCbc, которой нет. Ошибка, которую я пытался обойти.
@Bouls - Если вы не «используете» текущую версию .NET, это необходимо указать! Откуда нам это знать? Итак, какую версию вы используете?
@Bouls - В вашей реализации отсутствует отключение заполнения по умолчанию PKCS # 7, см. раздел «Редактирование» в моем ответе.
Извини за это ! Текущая версия — 4.8, однако у меня ошибка была только в файле aes.DecryptCbc.
Без детального анализа кода кажется, что во втором тесте (который проверяет случай успеха) уже происходит сбой сравнения MAC (т.е. аутентификации), которое выполняется до фактической расшифровки, т.е. либо реализация, либо тестовые данные. неисправен. Вам следует сначала решить эту проблему. Даже если дешифрование реализовано правильно, нельзя ожидать успешного дешифрования, если аутентификация не удалась.