using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using System.Collections.Generic;
using Org.BouncyCastle.Utilities;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using Org.BouncyCastle.Security;
private readonly byte[] keyMAC;
private readonly byte[] keyENC;
private const byte PAD = 0x80;
public static void Main()
string Kenc = "3d7155f3791c313c2924f51ae60f1ac9";
string Kmac = "b5feb9488f17be03e54c7d80907e8a1f";
string scc = "0000000000000000000000000000000A";
string responseSM1 = "990290008e08b2acb18b3e110c5c9000";
string apduResonse = decryptAPDU(responseSM1, Kenc, Kmac, scc);
Console.WriteLine(" Expected APDU Response: 9000. Decrypted APDU Response: " + apduResonse);
scc = "0000000000000000000000000000000C";
responseSM1 = "8711019dbc106daf0fd4f5cea58f5109153f84990290008e08bc40d8ffe82ccb709000";
apduResonse = decryptAPDU(responseSM1, Kenc, Kmac, scc);
Console.WriteLine(" Expected APDU Response : 6e60315e309000. Decrypted APDU Response: " + apduResonse);
public static string decryptAPDU(string apdu, string keyEnc, string keyMac, string scc)
string dechipherApdu = "";
byte[] apduBytes = StringUtils.ToByteArray(apdu);
byte[] KeyENC = StringUtils.ToByteArray(keyEnc);
byte[] KeyMAC = StringUtils.ToByteArray(keyMac);
SecureMessaging sm = new SecureMessaging(KeyMAC, KeyENC);
dechipherApdu = byteToHexStr(sm.decrypt(apduBytes, StringUtils.ToByteArray(scc)));
private CMac getCMAC(byte[] smssc)
CMac cmac = new CMac(new AesEngine());
cmac.Init(new KeyParameter(keyMAC));
cmac.BlockUpdate(smssc, 0, smssc.Length);
public static string byteToHexStr(byte[] bytes)
for (int i = 0; i < bytes.Length; i++)
returnStr += bytes[i].ToString("X2");
private byte[] pad(byte[] data, int blockSize)
byte[] result = new byte[data.Length + (blockSize - data.Length % blockSize)];
Array.Copy(data, 0, result, 0, data.Length);
result[data.Length] = PAD;
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();
int size = bais.ReadByte();
byte[] sizeBytes = new byte[size & 0x0F];
bais.Read(sizeBytes, 0, sizeBytes.Length);
size = new BigInteger(1, sizeBytes).IntValue;
bais.Seek(1, SeekOrigin.Current);
dataObject = new byte[size - 1];
bais.Read(dataObject, 0, dataObject.Length);
tag = (byte)bais.ReadByte();
if (bais.ReadByte() == 0x02)
bais.Read(statusBytes, 0, 2);
tag = (byte)bais.ReadByte();
throw new IOException("Malformed Secure Messaging APDU");
if (bais.ReadByte() == 0x08)
bais.Read(macObject, 0, 8);
throw new IOException("Malformed Secure Messaging APDU");
if (bais.Length - bais.Position != 2)
throw new IOException("Malformed Secure Messaging APDU");
Org.BouncyCastle.Crypto.Macs.CMac cmac = getCMAC(secureMessagingSSC);
byte[] mac = new byte[16];
using (MemoryStream macData = new MemoryStream())
TLV paddedDataObject = new TLV();
paddedDataObject.SetTagNumWithClass(0x87);
paddedDataObject.SetValue(ByteUtils.Concatenate(new byte[] { 0x01 }, dataObject));
macData.Write(paddedDataObject.ToBER(), 0, paddedDataObject.ToBER().Length);
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);
mac = ByteUtils.Copy(mac, 0, 8);
baos.Write(statusBytes, 0, statusBytes.Length);
private TagLengthValue tag;
private TLV child = null;
tag = new TagLengthValue();
this.next = (obj.next != null) ? new TLV(obj.next) : null;
this.child = (obj.child != null) ? new TLV(obj.child) : null;
return this.tag.GetTag();
public void SetTag(Tag tag)
public TagClass GetTagClass()
return tag.GetTagClass();
public void SetTagClass(TagClass tagClass)
tag.SetTagClass(tagClass);
public bool IsPrimitive()
return tag.IsPrimitive();
public void SetPrimitive(bool primitive)
tag.SetPrimitive(primitive);
public void SetTagNum(byte tagNum)
SetTagNum(tagNum & 0xFF);
public void SetTagNum(long tagNum)
public long GetTagNumWithClass()
return tag.GetTagNumWithClass();
public void SetTagNumWithClass(byte tagNumWithClass)
SetTagNumWithClass(tagNumWithClass & 0xFF);
public void SetTagNumWithClass(long tagNumWithClass)
tag.SetTagNumWithClass(tagNumWithClass);
public void SetTagNumWithClass(byte[] tagNumWithClass)
tag.SetTagNumWithClass(tagNumWithClass);
public int GetValueLength()
return tag.GetValueLength();
public void SetValue(byte[] value)
public void AddToEnd(TLV sibling)
else if (n > 0 && next != null)
return next.Remove(n - 1);
public void SetChild(TLV child)
public List<TLV> AsList()
LinkedList<TLV> result = new LinkedList<TLV>();
TLV toAdd = new TLV(nextTag);
public List<TLV> FindNextTags(long num)
List<TLV> all = AsList();
LinkedList<TLV> result = new LinkedList<TLV>();
foreach (TLV nextTLV in all)
if (nextTLV.GetTagNumWithClass() == num)
public List<TLV> FindChildTags(long num)
return GetChild().FindNextTags(num);
public static TLV FromBER(byte[] input)
next.tag = TagLengthValue.FromBER(rest);
if (!next.tag.IsPrimitive() && next.tag.GetValueLength() > 0)
next.child = FromBER(next.tag.GetValue());
rest = last.tag.ExtractRest(rest);
public byte[] ToBER(bool withSuccessors)
using (MemoryStream outStream = new MemoryStream())
ToBER(outStream, withSuccessors);
return outStream.ToArray();
throw new Exception("IOException: " + ex.Message);
private void ToBER(MemoryStream outStream, bool withSuccessors)
byte[] childBytes = child.ToBER(true);
tag.SetValue(childBytes);
outStream.Write(tag.ToBER(), 0, tag.ToBER().Length);
if (withSuccessors && next != null)
next.ToBER(outStream, withSuccessors);
public override string ToString()
public string ToString(string prefix)
string result = prefix + String.Format("{0:X2}", GetTagNumWithClass());
result += " " + tag.GetValueLength() + " " + BitConverter.ToString(tag.GetValue()).Replace("-", "");
result += "\n" + GetChild().ToString(prefix + " ");
result += "\n" + GetNext().ToString(prefix);
public class TagLengthValue
public TagLengthValue(int numOctets, Tag tag, byte[] value)
this.numOctets = numOctets;
public TagLengthValue(int numOctets, TagClass tagClass, bool primitive, long tagNum, byte[] value)
this.numOctets = numOctets;
tag = new Tag(tagClass, primitive, tagNum);
public TagLengthValue(Tag tag, byte[] value)
public TagLengthValue(TagClass tagClass, bool primitive, long tagNum, byte[] value)
tag = new Tag(tagClass, primitive, tagNum);
this.value = new byte[] { };
return new Tag(this.tag);
public void SetTag(Tag tag)
public TagClass GetTagClass()
return this.tag.GetTagClass();
public void SetTagClass(TagClass tagClass)
this.tag.SetTagClass(tagClass);
public bool IsPrimitive()
return this.tag.IsPrimitive();
public void SetPrimitive(bool primitive)
this.tag.SetPrimitive(primitive);
return this.tag.GetTagNum();
public void SetTagNum(long tagNum)
this.tag.SetTagNum(tagNum);
public long GetTagNumWithClass()
return this.tag.GetTagNumWithClass();
public void SetTagNumWithClass(long tagNumWithClass)
this.tag.SetTagNumWithClass(tagNumWithClass);
public void SetTagNumWithClass(byte[] tagNumWithClass)
this.tag = Tag.FromBER(tagNumWithClass);
public int GetValueLength()
return this.value.Length;
public void SetValue(byte[] value)
public byte[] ExtractRest(byte[] inputWithThisTag)
int from = GetRawLength();
int to = inputWithThisTag.Length;
return Arrays.CopyOfRange(inputWithThisTag, from, to);
catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException)
throw new TLVException("Data length and claimed length do not match.", ex);
public static TagLengthValue FromBER(byte[] data)
Tag tag = Tag.FromBER(data);
int numOctets = tag.numOctets;
if (((data[numOctets] >> 7) & 0x01) == 0)
dataLength = data[numOctets];
if ((data[numOctets] & 0x7F) == 0x00)
if (data.Length <= numOctets + i)
throw new TLVException("Not enough bytes in input to read TLV length.");
byte next = data[numOctets + i];
int numLengthBytes = data[numOctets] & 0x7F;
while (i < numLengthBytes)
throw new TLVException("Length doesn't fit into a 32 bit word.");
else if (data.Length < numOctets + i + 1)
throw new TLVException("Not enough bytes in input to read TLV length.");
if (data[numOctets + i] < 0)
dataLength = (dataLength << 8) | (256 + data[numOctets + i]);
dataLength = (dataLength << 8) | data[numOctets + i];
byte[] dataField = Arrays.CopyOfRange(data, numOctets, numOctets + dataLength);
numOctets = numOctets + dataLength;
TagLengthValue result = new TagLengthValue(numOctets, tag, dataField);
using (MemoryStream outStream = new MemoryStream())
byte[] tagBytes = tag.ToBER();
outStream.Write(tagBytes, 0, tagBytes.Length);
int len = GetValueLength();
outStream.WriteByte((byte)len);
byte[] lenBytes = BitConverter.GetBytes(len);
if (BitConverter.IsLittleEndian)
byte lenHeader = (byte)(0x80 | lenBytes.Length);
outStream.WriteByte(lenHeader);
outStream.Write(lenBytes, 0, lenBytes.Length);
outStream.Write(GetValue(), 0, GetValue().Length);
return outStream.ToArray();
throw new Exception("An error occurred while writing to the stream.", ex);
public static class ByteUtils
public static byte[] Clone(byte[] input)
byte[] ret = new byte[input.Length];
Array.Copy(input, 0, ret, 0, input.Length);
public static byte[] Concatenate(byte[] x, byte[] y)
byte[] ret = new byte[x.Length + y.Length];
Array.Copy(x, 0, ret, 0, x.Length);
Array.Copy(y, 0, ret, x.Length, y.Length);
public static byte[] Concatenate(byte x, byte[] y)
return Concatenate(new byte[] { x }, y);
public static byte[] Concatenate(byte[] x, byte y)
return Concatenate(x, new byte[] { y });
public static byte[] Concatenate(byte x, byte y)
return Concatenate(new byte[] { x }, new byte[] { y });
public static long ToLong(byte[] bytes, bool bigEndian)
if (bytes.Length > 8 || bytes.Length < 1)
throw new ArgumentException("La taille du tableau d'octets doit être comprise entre 1 et 8.");
for (int i = 0; i < bytes.Length; i++)
result |= ((long)0xFF & bytes[bytes.Length - 1 - i]) << i * 8;
for (int i = 0; i < bytes.Length; i++)
result |= ((long)0xFF & bytes[i]) << i * 8;
public static long ToLong(byte[] bytes)
return ToLong(bytes, true);
public static byte[] Copy(byte[] input, int offset, int length)
byte[] tmp = new byte[length];
Array.Copy(input, offset, tmp, 0, length);
public static string ToHexString(byte[] bytes, bool formatted)
return ToHexString(bytes, formatted, false);
public static string ToHexString(byte[] bytes, bool formatted, bool addLinebreak)
return ToHexString(bytes, "0x%02X ", addLinebreak);
return ToHexString(bytes, "%02X", addLinebreak);
private static string ToHexString(byte[] bytes, string format, bool addLinebreak)
StringBuilder builder = new StringBuilder(bytes.Length * 2);
for (int i = 1; i <= bytes.Length; i++)
builder.AppendFormat(format, bytes[i - 1]);
if (addLinebreak && i % 16 == 0)
return builder.ToString();
public static bool Compare(byte[] x, byte[] y)
if (y == null || x == null)
if (x.Length != y.Length)
return ByteUtils.AreEqual(x, y);
public static string byteToHexStr(byte[] bytes)
for (int i = 0; i < bytes.Length; i++)
returnStr += bytes[i].ToString("X2");
public static byte[] StringToByteArray(string hex)
return Enumerable.Range(0, hex.Length)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
public static bool AreEqual(byte[] x, byte[] y)
if (x.Length != y.Length)
for (int i = 0; i < x.Length; i++)
public static readonly Tag BOOLEAN_TAG = new Tag(TagClass.UNIVERSAL, true, 1);
public static readonly Tag INTEGER_TAG = new Tag(TagClass.UNIVERSAL, true, 2);
public static readonly Tag BITSTRING_TAG = new Tag(TagClass.UNIVERSAL, true, 3);
public static readonly Tag OCTETSTRING_TAG = new Tag(TagClass.UNIVERSAL, true, 4);
public static readonly Tag NULL_TAG = new Tag(TagClass.UNIVERSAL, true, 5);
public static readonly Tag OID_TAG = new Tag(TagClass.UNIVERSAL, true, 6);
public static readonly Tag REAL_TAG = new Tag(TagClass.UNIVERSAL, true, 9);
public static readonly Tag ENUMERATED_TAG = new Tag(TagClass.UNIVERSAL, true, 10);
public static readonly Tag EMBEDDED_PDV_TAG = new Tag(TagClass.UNIVERSAL, false, 11);
public static readonly Tag RELATIVE_OID_TAG = new Tag(TagClass.UNIVERSAL, true, 13);
public static readonly Tag SEQUENCE_TAG = new Tag(TagClass.UNIVERSAL, false, 16);
public static readonly Tag SET_TAG = new Tag(TagClass.UNIVERSAL, false, 17);
private TagClass tagClass;
private long tagNumWithClass;
public int numOctets = 0;
this.tagClass = TagClass.UNIVERSAL;
CalculateTagNumWithClass();
this.tagClass = tag.tagClass;
this.primitive = tag.primitive;
this.tagNum = tag.tagNum;
CalculateTagNumWithClass();
public Tag(TagClass tagClass, bool primitive, long tagNum)
this.tagClass = tagClass;
this.primitive = primitive;
CalculateTagNumWithClass();
public TagClass GetTagClass()
public void SetTagClass(TagClass tagClass)
this.tagClass = tagClass;
CalculateTagNumWithClass();
public bool IsPrimitive()
public void SetPrimitive(bool primitive)
this.primitive = primitive;
CalculateTagNumWithClass();
public void SetTagNum(long tagNum)
CalculateTagNumWithClass();
public long GetTagNumWithClass()
return this.tagNumWithClass;
public void SetTagNumWithClass(long tagNumWithClass)
Tag newTag = Tag.FromBER(LongUtils.ToByteArray(tagNumWithClass));
this.tagClass = newTag.tagClass;
this.primitive = newTag.primitive;
this.tagNum = newTag.tagNum;
this.tagNumWithClass = tagNumWithClass;
private void CalculateTagNumWithClass()
byte leading = (byte)tagClass;
leading = (byte)((leading << 1) | ((this.primitive) ? 0 : 1));
leading = (byte)((leading << 5) | 0x1F);
rest = LongUtils.ToByteArray(this.tagNum, 7);
for (int i = 0; i < rest.Length - 1; i++)
leading = (byte)((leading << 5) | (byte)this.tagNum);
byte[] resultBytes = ByteUtils.Concatenate(leading, rest);
this.tagNumWithClass = ByteUtils.ToLong(resultBytes);
public static Tag FromBER(byte[] data)
TagClass tagClass = (TagClass)data[0];
bool primitive = ((data[0] >> 5) & 0x01) == 0x00;
byte tmpTagNum = (byte)(data[0] & 0x1F);
throw new TLVException("Tag number doesn't fit into a 64 bit word.");
else if (data.Length < numOctets)
throw new TLVException("Not enough bytes in input bytes to build TLV tag.");
byte nextValue = (byte)(next & 0x7F);
tagNum = (tagNum << 7) | nextValue;
} while (((next >> 7) & 0x01) == 0x01);
Tag resultTag = new Tag(tagClass, primitive, tagNum);
resultTag.numOctets = numOctets;
return LongUtils.ToByteArray(tagNumWithClass);
public override string ToString()
return "[" + tagClass.ToString() + " " +
(primitive ? "prim " : "cons ") +
tagNum + " (0x" + tagNumWithClass.ToString("X") + ")]";
public override bool Equals(object obj)
return this.GetTagNumWithClass() == other.GetTagNumWithClass();
public override int GetHashCode()
hash = 79 * hash + (int)(this.tagNumWithClass ^ (this.tagNumWithClass >> 32));
public class TLVException : Exception
public TLVException(string message) : base(message)
public TLVException(string message, Exception innerException) : base(message, innerException)
public static byte[] ToByteArray(long value)
return ToByteArray(value, 8);
public static byte[] ToByteArray(long value, int numBits)
return ToByteArray(value, numBits, true);
public static byte[] ToByteArray(long value, int numBits, bool bigEndian)
if (numBits <= 0 || numBits > 8)
throw new ArgumentException("Numbits must be between 0 and 8.");
int numBytesInBuffer = 64 / numBits;
int restBits = 64 - (numBytesInBuffer * numBits);
for (int i = numBytesInBuffer - ((restBits > 0) ? 0 : 1); i >= 0; i--)
if (i == numBytesInBuffer)
byte mask = NumBitsToMask((byte)restBits);
b = (byte)((byte)(value >> (((i - 1) * numBits) + restBits)) & mask);
byte mask = NumBitsToMask((byte)numBits);
b = (byte)((value >> (i * numBits)) & mask);
if (buffer == null && b != 0)
buffer = new byte[i + 1];
private static byte NumBitsToMask(byte numBits)
for (byte i = 0; i < numBits; i++)
result = (byte)((result << 1) | 1);
public static byte[] ToByteArray(long value, bool padArrayToTypeLength)
return ToByteArray(value, padArrayToTypeLength, true);
public static byte[] ToByteArray(long value, bool padArrayToTypeLength, bool bigEndian)
byte[] result = ToByteArray(value, 8, bigEndian);
if (padArrayToTypeLength && result.Length < 8)
byte[] padding = new byte[8 - result.Length];
result = Concatenate(padding, result);
private static byte[] Concatenate(byte[] array1, byte[] array2)
byte[] result = new byte[array1.Length + array2.Length];
Array.Copy(array1, 0, result, 0, array1.Length);
Array.Copy(array2, 0, result, array1.Length, array2.Length);
private static readonly Regex WS_PATTERN = new Regex("\\s");
public static byte[] ToByteArray(string hex)
if ((hex.Length % 2) != 0)
byte[] ret = new byte[hex.Length / 2];
for (int i = 0; i < ret.Length; i++)
ret[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
public static byte[] ToByteArray(string hex, bool removeWhitespace)
hex = WS_PATTERN.Replace(hex, "");
public static bool IsNullOrEmpty(string value)
return string.IsNullOrEmpty(value);
public static string NullToEmpty(string s)
public static string EmptyToNull(string s)
return string.IsNullOrEmpty(s) ? null : s;
internal class SecureMessaging
private readonly byte[] NULL = new byte[] { 0x00 };
private const byte PAD = 0x80;
private readonly byte[] secureMessagingSSC;
private readonly byte[] keyMAC;
private readonly byte[] keyENC;
public SecureMessaging(byte[] keyMAC, byte[] keyENC)
secureMessagingSSC = new byte[16];
public byte[] encrypt(byte[] apdu)
incrementSSC(secureMessagingSSC);
byte[] commandAPDU = encrypt(apdu, secureMessagingSSC);
incrementSSC(secureMessagingSSC);
public byte[] encrypt(byte[] apdu, byte[] secureMessagingSSC)
Console.WriteLine("secureMessagingSSC : " + i + " " + ByteUtils.byteToHexStr(secureMessagingSSC));
using (MemoryStream baos = new MemoryStream())
CardCommandAPDU cAPDU = new CardCommandAPDU(apdu);
if (cAPDU.IsSecureMessaging())
throw new ArgumentException("APDU malformé.");
byte[] data = cAPDU.GetData();
byte[] header = cAPDU.getHeader();
byte[] dataEncrypted = Array.Empty<byte>();
using (Aes aes = Aes.Create())
aes.IV = getCipherIV(secureMessagingSSC);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
dataEncrypted = encryptor.TransformFinalBlock(data, 0, data.Length);
dataEncrypted = new byte[] { 0x01 }.Concat(dataEncrypted).ToArray();
dataEncrypted = new byte[] { 0x87, (byte)dataEncrypted.Length }.Concat(dataEncrypted).ToArray();
TLV leObject = new TLV();
leObject.SetTagNumWithClass(0x97);
leObject.SetValue(new byte[] { (byte)((le >> 8) & 0xFF), (byte)(le & 0xFF) });
leObject.SetValue(new byte[] { (byte)le });
baos.Write(leObject.ToBER(), 0, leObject.ToBER().Length);
byte[] mac = new byte[16];
CMac cmac = getCMAC(secureMessagingSSC);
byte[] paddedHeader = pad(header, 16);
cmac.BlockUpdate(paddedHeader, 0, paddedHeader.Length);
if (dataEncrypted.Length > 0)
byte[] paddedData = pad(dataEncrypted, 16);
cmac.BlockUpdate(paddedData, 0, paddedData.Length);
mac = mac.Take(8).ToArray();
mac = new byte[] { 0x8e, (byte)mac.Length }.Concat(mac).ToArray();
byte[] secureData = dataEncrypted.Concat(mac).ToArray();
byte[] secureCommand = header
.Concat(new byte[] { (byte)secureData.Length })
.Concat(new byte[] { 0x00 })
public byte[] decrypt(byte[] response)
if (response.Length < 12)
throw new ArgumentException("Malformed Secure Messaging APDU.");
return decrypt(response, secureMessagingSSC);
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();
int size = bais.ReadByte();
byte[] sizeBytes = new byte[size & 0x0F];
bais.Read(sizeBytes, 0, sizeBytes.Length);
size = new BigInteger(1, sizeBytes).IntValue;
bais.Seek(1, SeekOrigin.Current);
dataObject = new byte[size - 1];
bais.Read(dataObject, 0, dataObject.Length);
tag = (byte)bais.ReadByte();
if (bais.ReadByte() == 0x02)
bais.Read(statusBytes, 0, 2);
tag = (byte)bais.ReadByte();
throw new IOException("Malformed Secure Messaging APDU");
if (bais.ReadByte() == 0x08)
bais.Read(macObject, 0, 8);
throw new IOException("Malformed Secure Messaging APDU");
if (bais.Length - bais.Position != 2)
throw new IOException("Malformed Secure Messaging APDU");
CMac cmac = getCMAC(secureMessagingSSC);
byte[] mac = new byte[16];
MemoryStream macData = new MemoryStream();
TLV paddedDataObject = new TLV();
paddedDataObject.SetTagNumWithClass(0x87);
paddedDataObject.SetValue(ByteUtils.Concatenate((byte)0x01, dataObject));
macData.Write(paddedDataObject.ToBER(), 0, paddedDataObject.ToBER().Length);
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);
mac = ByteUtils.Copy(mac, 0, 8);
if (!ByteUtils.Compare(mac, macObject))
throw new GeneralSecurityException("Secure Messaging MAC verification failed");
baos.Write(statusBytes, 0, statusBytes.Length);
public static void incrementSSC(byte[] ssc)
for (int i = ssc.Length - 1; i >= 0; i--)
private byte[] pad(byte[] data, int blockSize)
byte[] result = new byte[data.Length + (blockSize - data.Length % blockSize)];
Array.Copy(data, 0, result, 0, data.Length);
result[data.Length] = PAD;
private byte[] getCipherIV(byte[] smssc)
using (Aes aes = Aes.Create())
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, null);
return encryptor.TransformFinalBlock(smssc, 0, smssc.Length);
private CMac getCMAC(byte[] smssc)
CMac cmac = new CMac(new AesEngine());
cmac.Init(new KeyParameter(keyMAC));
cmac.BlockUpdate(smssc, 0, smssc.Length);
public class CardCommandAPDU : CardAPDU
public readonly byte[] header = new byte[4];
protected CardCommandAPDU()
public CardCommandAPDU(byte[] commandAPDU)
System.Array.Copy(commandAPDU, 0, header, 0, 4);
setBody(ByteUtils.Copy(commandAPDU, 4, commandAPDU.Length - 4));
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, byte le)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, short le)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, int le)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, byte[] data)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, byte[] data, byte le)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, byte[] data, short le)
public CardCommandAPDU(byte cla, byte ins, byte p1, byte p2, byte[] data, int le)
protected void SetCLA(byte cla)
return (byte)(header[0] & 0xFF);
protected void setINS(byte ins)
return (byte)(header[1] & 0xFF);
protected void setP1(byte p1)
return (byte)(header[2] & 0xFF);
protected void setP2(byte p2)
return (byte)(header[3] & 0xFF);
protected void setP1P2(byte[] p1p2)
return new byte[] { getP1(), getP2() };
public byte[] getHeader()
public static byte[] getHeader(byte[] commandAPDU)
if (commandAPDU.Length < 4)
throw new ArgumentException("APDU mal formée");
return ByteUtils.Copy(commandAPDU, 0, 4);
protected void setLC(byte lc)
setLC((byte)(lc & 0xFF));
protected void setLC(short lc)
setLC((byte)(lc & 0xFFFF));
protected void setLC(int lc)
if (lc < 1 || lc > 65535)
throw new ArgumentException("La longueur doit être comprise entre '1' et '65535'.");
public void SetData(byte[] data)
public void setBody(byte[] body)
using (MemoryStream bais = new MemoryStream(body))
int length = (int)bais.Length;
le = bais.ReadByte() & 0xFF;
int tmp = bais.ReadByte();
le = ((bais.ReadByte() & 0xFF) << 8) | (bais.ReadByte() & 0xFF);
lc = ((bais.ReadByte() & 0xFF) << 8) | (bais.ReadByte() & 0xFF);
le = bais.ReadByte() & 0xFF;
else if (bais.Length == 2)
le = ((bais.ReadByte() & 0xFF) << 8) | (bais.ReadByte() & 0xFF);
else if (bais.Length == 3)
if (bais.ReadByte() == 0)
le = ((bais.ReadByte() & 0xFF) << 8) | (bais.ReadByte() & 0xFF);
throw new ArgumentException("APDU malformée.");
else if (bais.Length > 3)
throw new ArgumentException("APDU malformée.");
SetLE((byte)bais.ReadByte());
else if (bais.Length == 3)
SetLE((byte)(short)(((bais.ReadByte() & 0xFF) << 8) | (bais.ReadByte() & 0xFF)));
else if (bais.Length == 2 || bais.Length > 3)
throw new ArgumentException("APDU malformée.");
throw new ArgumentException("APDU malformée.");
throw new ArgumentException("APDU malformée.");
Console.WriteLine("Exception", e);
public static byte[] getBody(byte[] commandAPDU)
if (commandAPDU.Length < 4)
throw new ArgumentException("APDU malformée");
return ByteUtils.Copy(commandAPDU, 4, commandAPDU.Length - 4);
public void setLE(byte le)
SetLE((byte)(le & 0xFF));
public void SetLE(short le)
public void SetLE(int le)
if (le < 0 || le > 65536)
throw new ArgumentException("La longueur doit être comprise entre '1' et '65535'.");
public void SetChaining()
header[0] = (byte)(header[0] | 0x10);
public void SetSecureMessaging()
header[0] = (byte)(header[0] | 0x0C);
public bool IsSecureMessaging()
return ((header[0] & (byte)0x0F) == (byte)0x0C);
public byte[] ToByteArray()
using (MemoryStream ms = new MemoryStream())
ms.Write(header, 0, header.Length);
if (lc > 255 || (le > 256 && lc > 0))
ms.WriteByte((byte)(lc >> 8));
ms.Write(data, 0, data.Length);
ms.WriteByte((byte)(le >> 8));
ms.WriteByte((byte)(le >> 8));
Console.WriteLine("Échec de la création de l'APDU en mémoire.", ex);
public override string ToString()
return ByteUtils.ToHexString(ToByteArray(), true);
protected static readonly byte x00 = 0x00;
protected static readonly byte xFF = 0xFF;
public byte[] data = new byte[0];
byte[] ret = new byte[data.Length];
System.Array.Copy(data, 0, ret, 0, data.Length);
public void setData(byte[] data)