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.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);
string scc = "0000000000000000000000000000000C";
string responseSM1 = "8711019dbc106daf0fd4f5cea58f5109153f84990290008e08bc40d8ffe82ccb709000";
string 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 = Convert.FromHexString(apdu);
byte[] KeyENC = Convert.FromHexString(keyEnc);
byte[] KeyMAC = Convert.FromHexString(keyMac);
SecureMessaging sm = new SecureMessaging(KeyMAC, KeyENC);
dechipherApdu = Convert.ToHexString(sm.decrypt(apduBytes, Convert.FromHexString(scc)));
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[] 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");
using (Aes aes = Aes.Create())
aes.IV = getCipherIV(secureMessagingSSC);
using (ICryptoTransform decryptor = aes.CreateDecryptor())
byte[] decryptedPadded = decryptor.TransformFinalBlock(dataObject, 0, dataObject.Length);
byte[] decrypted = Unpad(decryptedPadded);
baos.Write(decrypted, 0, decrypted.Length);
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 static byte[] Unpad(byte[] data)
for (int i = data.Length - 1; i >= 0; i--)
return ByteUtils.Copy(data, 0, i);
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);
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 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 = GetTagClass(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 static TagClass GetTagClass(byte octet)
byte classByte = (byte)((octet >> 6) & 0x03);
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);
public class TLVException : Exception
public TLVException(string message) : base(message)
public TLVException(string message, Exception innerException) : base(message, innerException)
public static class ByteUtils
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 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);