Minifycode 2023-02-08 Viewed 421 times C#

How to create DUKPT (Derived Unique Key Per Transaction) in c#

In this article you will learn, How to create DUKPT (Derived Unique Key Per Transaction) in c#?. DUKPT is a key management scheme which is widely used for encryption and decryption of credit card data in the Payment industry. This scheme ensures the security of encrypted data by generating a unique per every single encryption. It maintains a counter which is incremented per transaction. DUKPT uses this counter to generate a one time encryption key which will be used to encrypt data. Since this counter is incremented each time an encryption happens, a new key is generated per each encryption.

Most common use case of DUKPT is to encrypt credit card information in Point of Sale devices/credit card readers. In this scenario, a new key is generated per each swipe of a credit card, which results in a different encrypted data for the same card per each swipe.

You can find more information about DUKPT in this nice article.
 
Therefore I decided to implement it by myself no library use.

KSN: The reader starts life with a unique 128-bit key, and then, each time a card is read, a counter increments. The counter is in a value called the Key Serial Number (KSN). Bear in mind, the KSN itself is public. (It will get sent, with the encrypted data, to the decrypting party, in a real-world decryption scenario.) At transaction time, the KSN is combined, using a special algorithm, with the reader’s original encryption key in a such a way as to derive a unique new key (using one-way hashes, so that if a particular key is ever stolen, it can’t be used to calculate any other keys). The algorithm in question is defined by ANSI X.9-24. It’s quite a clever symmetric key-management scheme, but it’s also a little tricky to implement. Which is why we’ve done it for you.

 

public static class Dukpt
    {
        #region Private Mask Constants
        public static BigInteger HexToBigInteger(this string str)
        {
            return BigInteger.Parse("00" + str, System.Globalization.NumberStyles.HexNumber);
        }
        public static byte[] GetBytes(this BigInteger number)
        {
            return number.ToByteArray().Reverse().SkipWhile(b => b == 0).ToArray();
        }
        public static BigInteger ToBigInteger(this byte[] bytes)
        {
            return new BigInteger(bytes.Reverse().Concat(new byte[] { 0 }).ToArray());
        }
        private static readonly BigInteger Reg3Mask = "1FFFFF".HexToBigInteger();
        private static readonly BigInteger ShiftRegMask = "100000".HexToBigInteger();
        private static readonly BigInteger Reg8Mask = "FFFFFFFFFFE00000".HexToBigInteger();
        private static readonly BigInteger Ls16Mask = "FFFFFFFFFFFFFFFF".HexToBigInteger();
        private static readonly BigInteger Ms16Mask = "FFFFFFFFFFFFFFFF0000000000000000".HexToBigInteger();
        private static readonly BigInteger KeyMask = "C0C0C0C000000000C0C0C0C000000000".HexToBigInteger();
        private static readonly BigInteger PekMask = "FF00000000000000FF".HexToBigInteger();
        private static readonly BigInteger KsnMask = "FFFFFFFFFFFFFFE00000".HexToBigInteger();
        private static readonly BigInteger DekMask = "0000000000FF00000000000000FF0000".HexToBigInteger();

        #endregion

        #region Private Methods
        public enum DUKPTVariant
        {
            PIN,
            Data
        }
        /// <summary>
        /// Create Initial PIN Encryption Key
        /// </summary>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="bdk">Base Derivation Key</param>
        /// <returns>Initial PIN Encryption Key</returns>
        private static BigInteger CreateIpek(BigInteger ksn, BigInteger bdk)
        {
            return Transform("TripleDES", true, bdk, (ksn & KsnMask) >> 16) << 64
                 | Transform("TripleDES", true, bdk ^ KeyMask, (ksn & KsnMask) >> 16);
        }

        /// <summary>
        /// Create Session Key with PEK Mask
        /// </summary>
        /// <param name="ipek">Initial PIN Encryption Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <returns>Session Key</returns>
        private static BigInteger CreateSessionKeyPEK(BigInteger ipek, BigInteger ksn)
        {
            return DeriveKey(ipek, ksn) ^ PekMask;
        }

        /// <summary>
        /// Create Session Key with DEK Mask
        /// </summary>
        /// <param name="ipek">Initial PIN Encryption Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <returns>Session Key</returns>
        private static BigInteger CreateSessionKeyDEK(BigInteger ipek, BigInteger ksn)
        {
            BigInteger key = DeriveKey(ipek, ksn) ^ DekMask;
            return Transform("TripleDES", true, key, (key & Ms16Mask) >> 64) << 64
                 | Transform("TripleDES", true, key, (key & Ls16Mask));
        }

        /// <summary>
        /// Create Session Key
        /// </summary>
        /// <param name="bdk">Base Derivation Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="DUKPTVariant">DUKPT variant used to determine session key creation method</param>
        /// <returns>Session Key</returns>
        private static BigInteger CreateSessionKey(string bdk, string ksn, DUKPTVariant DUKPTVariant)
        {
            BigInteger ksnBigInt = ksn.HexToBigInteger();
            BigInteger ipek = CreateIpek(ksnBigInt, bdk.HexToBigInteger());
            BigInteger sessionKey;
            if (DUKPTVariant == DUKPTVariant.Data)
            {
                sessionKey = CreateSessionKeyDEK(ipek, ksnBigInt);
            }
            else
            {
                sessionKey = CreateSessionKeyPEK(ipek, ksnBigInt);
            }
            return sessionKey;
        }

        /// <summary>
        /// Derive Key from IPEK and KSN
        /// </summary>
        /// <param name="ipek">Initial PIN Encryption Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <returns>Key derived from IPEK and KSN</returns>
        private static BigInteger DeriveKey(BigInteger ipek, BigInteger ksn)
        {
            BigInteger ksnReg = ksn & Ls16Mask & Reg8Mask;
            BigInteger curKey = ipek;
            for (BigInteger shiftReg = ShiftRegMask; shiftReg > 0; shiftReg >>= 1)
            {
                if ((shiftReg & ksn & Reg3Mask) > 0)
                {
                    ksnReg = ksnReg | shiftReg;
                    curKey = GenerateKey(curKey, ksnReg);
                }
            }
            return curKey;
        }

        /// <summary>
        /// Generate Key
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <returns>Key generated from provided key and KSN</returns>
        private static BigInteger GenerateKey(BigInteger key, BigInteger ksn)
        {
            return EncryptRegister(key ^ KeyMask, ksn) << 64 | EncryptRegister(key, ksn);
        }

        /// <summary>
        /// Encrypt Register
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="reg8">Register which to encrypt</param>
        /// <returns>Encrypted register value</returns>
        private static BigInteger EncryptRegister(BigInteger key, BigInteger reg8)
        {
            return (key & Ls16Mask) ^ Transform("DES", true, (key & Ms16Mask) >> 64, (key & Ls16Mask ^ reg8));
        }

        /// <summary>
        /// Transform Data
        /// </summary>
        /// <param name="name">Encryption algorithm name</param>
        /// <param name="encrypt">Encrypt data flag</param>
        /// <param name="key">Encryption key</param>
        /// <param name="message">Data to encrypt or decrypt</param>
        /// <returns>Result of transformation (encryption or decryption)</returns>
        private static BigInteger Transform(string name, bool encrypt, BigInteger key, BigInteger message)
        {
            using (SymmetricAlgorithm cipher = SymmetricAlgorithm.Create(name))
            {
                byte[] k = key.GetBytes();
                cipher.Key = new byte[Math.Max(0, GetNearestWholeMultiple(k.Length, 8) - k.Length)].Concat(key.GetBytes()).ToArray();
                cipher.IV = new byte[8];
                cipher.Mode = CipherMode.ECB;
                cipher.Padding = PaddingMode.None;
                using (ICryptoTransform crypto = encrypt ? cipher.CreateEncryptor() : cipher.CreateDecryptor())
                {
                    byte[] data = message.GetBytes();
                    data = new byte[Math.Max(0, GetNearestWholeMultiple(data.Length, 8) - data.Length)].Concat(message.GetBytes()).ToArray();
                    return crypto.TransformFinalBlock(data, 0, data.Length).ToBigInteger();
                }
            }
        }

        /// <summary>
        /// Get nearest whole value of provided decimal value which is a multiple of provided integer
        /// </summary>
        /// <param name="input">Number which to determine nearest whole multiple</param>
        /// <param name="multiple">Multiple in which to divide input</param>
        /// <returns>Whole integer value of input nearest to a multiple of provided decimal</returns>
        private static int GetNearestWholeMultiple(decimal input, int multiple)
        {
            decimal output = Math.Round(input / multiple);
            if (output == 0 && input > 0)
            {
                output += 1;
            }
            output *= multiple;
            return (int)output;
        }

        #endregion

        #region Public Methods
        public static BigInteger dukptnew(BigInteger ipek, BigInteger ksn)
        {
            return DeriveKey(ipek, ksn);
        }
        /// <summary>
        /// Encrypt data using TDES DUKPT.
        /// </summary>
        /// <param name="bdk">Base Derivation Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="data">Data to encrypt</param>
        /// <param name="variant">DUKPT transaction key variant</param>
        /// <returns>Encrypted data</returns>
        /// <exception cref="ArgumentNullException">Thrown for null or empty parameter values</exception>
        public static byte[] Encrypt(string bdk, string ksn, byte[] data, DUKPTVariant variant)
        {
            if (string.IsNullOrEmpty(bdk))
            {
                throw new ArgumentNullException(nameof(bdk));
            }
            if (string.IsNullOrEmpty(ksn))
            {
                throw new ArgumentNullException(nameof(ksn));
            }
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            return Transform("TripleDES", true, CreateSessionKey(bdk, ksn, variant), data.ToBigInteger()).GetBytes();
        }

        /// <summary>
        /// Encrypt data using TDES DUKPT PIN variant.
        /// </summary>
        /// <param name="bdk">Base Derivation Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="data">Data to encrypt</param>
        /// <returns>Encrypted data</returns>
        /// <exception cref="ArgumentNullException">Thrown for null or empty parameter values</exception>
        public static byte[] Encrypt(string bdk, string ksn, byte[] data)
        {
            return Encrypt(bdk, ksn, data, DUKPTVariant.PIN);
        }

        /// <summary>
        /// Decrypt data using TDES DUKPT.
        /// </summary>
        /// <param name="bdk">Base Derivation Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="encryptedData">Data to decrypt</param>
        /// <param name="variant">DUKPT transaction key variant</param>
        /// <returns>Decrypted data</returns>
        /// <exception cref="ArgumentNullException">Thrown for null or empty parameter values</exception>
        public static byte[] Decrypt(string bdk, string ksn, byte[] encryptedData, DUKPTVariant variant)
        {
            if (string.IsNullOrEmpty(bdk))
            {
                throw new ArgumentNullException(nameof(bdk));
            }
            if (string.IsNullOrEmpty(ksn))
            {
                throw new ArgumentNullException(nameof(ksn));
            }
            if (encryptedData == null)
            {
                throw new ArgumentNullException(nameof(encryptedData));
            }

            return Transform("TripleDES", false, CreateSessionKey(bdk, ksn, variant), encryptedData.ToBigInteger()).GetBytes();
        }

        /// <summary>
        /// Decrypt data using TDES DUKPT PIN variant.
        /// </summary>
        /// <param name="bdk">Base Derivation Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="encryptedData">Data to decrypt</param>
        /// <returns>Decrypted data</returns>
        /// <exception cref="ArgumentNullException">Thrown for null or empty parameter values</exception>
        public static byte[] Decrypt(string bdk, string ksn, byte[] encryptedData)
        {
            return Decrypt(bdk, ksn, encryptedData, DUKPTVariant.PIN);
        }

        /// <summary>
        /// Decrypt data using TDES DUKPT Data variant.
        /// Backwards-compatible with previous versions of Dukpt.NET.
        /// </summary>
        /// <param name="bdk">Base Derivation Key</param>
        /// <param name="ksn">Key Serial Number</param>
        /// <param name="data">Data to decrypt</param>
        /// <returns>Decrypted data</returns>
        public static byte[] DecryptIdTech(string bdk, string ksn, byte[] encryptedData)
        {
            return Decrypt(bdk, ksn, encryptedData, DUKPTVariant.Data);
        }
        public static string Track2DataLength(string track2data)
        {
            string ret = "";
            if(track2data.Length>37)
            {
                track2data = track2data.Substring(0,37);
            }
            int tLength = track2data.Length;
            int devideBy = 8;
            int remainder = tLength % devideBy;
            int prefix = devideBy - remainder;
            if (remainder > 0)
            {
                for (int i = 0; i < prefix; i++)
                {
                    ret += "0";
                }
            }
            return ret+ track2data;
        }
        #endregion

    }

How to create DUKPT (Derived Unique Key Per Transaction) in c#
C# is a programming language developed by Microsoft that runs on the .NET Framework. C# is used to develop web, desktop, mobile, games and much more application. C# is a object-oriented programming language developed by Microsoft within its .NET Framework. Led by Anders Hejlsberg, your basic C# programming and will also take you through various advanced concepts related to C# programming language. C# such as control statements, objects and classes, inheritance, constructor, destructor, this, static, sealed, polymorphism, abstraction, abstract class, interface, File IO, Collections, namespace, encapsulation, properties, indexer, arrays, strings, regex, exception handling, multithreading etc. For example... using System; namespace MinifyCode { class Program { static void Main(string[] args) { Console.WriteLine("Hello Minify Code"); } } } Output: Hello Minify Code In this article you will learn, what is server side controls. We will discuss each of these objects in due time. In this tutorial we will explore the Server object, the Request object, and the Response object. Session Application Cache Request Response Server User Trace Server Object The Server object in Asp.NET is an instance of the System.Web.HttpServerUtility class. The HttpServerUtility class provides numerous properties and methods to perform many type of jobs. Methods and Properties of the Server object The methods and properties of the HttpServerUtility class are exposed through the intrinsic Server object provided by ASP.NET. using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Optimization; using System.Web.Routing; using System.Web.Security; using System.Web.SessionState; using System.Data.Entity; namespace minifycode { public class Global : HttpApplication { void Application_Start(object sender, EventArgs e) { // Code that runs on application startup RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // Initialize the product database. Database.SetInitializer(new ProductDatabaseInitializer()); // Create custom role and user. RoleActions roleActions = new RoleActions(); roleActions.AddUserAndRole(); // Add Routes. RegisterCustomRoutes(RouteTable.Routes); } void RegisterCustomRoutes(RouteCollection routes) { routes.MapPageRoute( "ProductsCategoryRoute", "Category/{categoryName}", "~/ProductList.aspx" ); routes.MapPageRoute( "ProductNameRoute", "Product/{productName}", "~/ProductDetails.aspx" ); } } }