我正在尝试从java到c#实现这个解密方法: 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
身份验证失败,因为您的
TLV
类和/或与其关联的类的实现存在缺陷。这可以通过直接实现该功能来轻松验证,一次用于 dataObject
:
byte[] tlv_dataObject = [0x01, ..dataObject];
tlv_dataObject = [0x87, (byte)tlv_dataObject.Length, ..tlv_dataObject];
macData.Write(tlv_dataObject);
一次
statusBytesObject
:
byte[] tlv_statusBytesObject = [0x99, (byte)statusBytes.Length, ..statusBytes];
macData.Write(tlv_statusBytesObject, 0, tlv_statusBytesObject.Length);
完成此更改后,身份验证成功。
请注意,上述代码不能替代
TLV
及其关联类的完整且正确的移植。为了使所有用例正常工作,必须移植所有必需的功能。
TLV
及其关联类实现数据的 ASN.1/DER 编码,请参阅此处。另一种移植方法是识别所有必需的功能并使用 C# 库中合适的 ASN.1/DER 编码器/解码器。
缺失的解密可以紧凑地实现,例如与
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);
}
这样修改后,密文就成功解密了。