private TLV Pack91(CryptoMetaData cryptoMetaData, byte[] arpc, TLV _8A)
        {
            TLV _91;

            if (arpc == null)
            {
                arpc = new byte[8];
            }

            switch (cryptoMetaData.SKDMethod)
            {
            case SKDMethod.VSDC:
                _91 = TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, _8A.Value));
                break;

            case SKDMethod.MCHIP:
                _91 = TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, _8A.Value));
                break;

            case SKDMethod.EMV_CSKD:
                _91 = TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, PackCSU()));
                break;

            default:
                throw new SimulatedPaymentProviderException("Pack91: SKDMethod not supported:" + cryptoMetaData.SKDMethod);
            }
            return(_91);
        }
Exemplo n.º 2
0
        internal bool ValidateHash()
        {
            byte[] concatenated = Formatting.ConcatArrays(
                new byte[] { CertificateFormat },
                IssuerIdentifier,
                ExpiryDate,
                CertificateSerialNumber,
                new byte[] { HashAlgorithmIndicator },
                new byte[] { PublicKeyAlgorithmIndicator },
                new byte[] { IssuerPublicKeyLength },
                new byte[] { IssuerPublicKeyExponentLength },
                IssuerPublicKeyorLeftmostDigitsofIssuerPublicKey,
                IssuerPublicKeyRemainder,
                Exponent
                );

            byte[] hash = SHA1.Create().ComputeHash(concatenated);

            if (Formatting.ByteArrayToHexString(HashResult) != Formatting.ByteArrayToHexString(hash))
            {
                return(false);
            }

            return(true);
        }
Exemplo n.º 3
0
        public virtual byte[] Serialize()
        {
            if (InstallParamC9 != null)
            {
                InstallParametersfield = InstallParamC9.Serialize();
            }
            else
            {
                InstallParametersfield = new byte[2] {
                    0xC9, 0x00
                }
            };

            return(Formatting.ConcatArrays(
                       new byte[] { BitConverter.GetBytes(ExecutableLoadFileAID.Length / 2)[0] },
                       Formatting.HexStringToByteArray(ExecutableLoadFileAID),
                       new byte[] { BitConverter.GetBytes(ExecutableModuleAID.Length / 2)[0] },
                       Formatting.HexStringToByteArray(ExecutableModuleAID),
                       new byte[] { BitConverter.GetBytes(ApplicationAID.Length / 2)[0] },
                       Formatting.HexStringToByteArray(ApplicationAID),
                       new byte[] { BitConverter.GetBytes(Privileges.Length)[0] },
                       Privileges,

                       ConvertLengthLength(InstallParametersfield.Length),
                       InstallParametersfield,
                       ConvertLengthLength(InstallToken.Length),
                       InstallToken
                       ));
        }
Exemplo n.º 4
0
        public static IKey FormDESKey(short keyLength, byte[] clearKeyBytes)
        {
            IKey key = null;

            switch (keyLength)
            {
            case SMAdapter.LENGTH_DES:
                key = new SecretKeySpec(clearKeyBytes, ALG_DES);
                break;

            case SMAdapter.LENGTH_DES3_2KEY:
                // make it 3 components to work with JCE
                clearKeyBytes = Formatting.ConcatArrays(clearKeyBytes, 0, GetBytesLength(SMAdapter.LENGTH_DES3_2KEY), clearKeyBytes, 0, GetBytesLength(SMAdapter.LENGTH_DES));
                key           = new SecretKeySpec(clearKeyBytes, ALG_TRIPLE_DES);
                break;

            case SMAdapter.LENGTH_DES3_3KEY:
                key = new SecretKeySpec(clearKeyBytes, ALG_TRIPLE_DES);
                break;
            }
            if (key == null)
            {
                throw new Exception("Unsupported DES key length: " + keyLength + " bits");
            }
            return(key);
        }
Exemplo n.º 5
0
        public virtual byte[] Serialize()
        {
            byte[] data;
            if (Data != null)
            {
                data = Data.Serialize();
            }
            else
            {
                data = DataBytes;
            }

            byte[] result = Formatting.ConcatArrays(
                DGI,
                new byte[] { BitConverter.GetBytes(data.Length)[0] },
                data
                );

            if (MAC != null)
            {
                result = Formatting.ConcatArrays(result, MAC);
            }

            return(result);
        }
Exemplo n.º 6
0
        private async Task <ApduResponse> Transceive(ApduCommand apduCommand)
        {
            ApduResponse apduRes = Activator.CreateInstance(apduCommand.ApduResponseType) as ApduResponse;

            byte[] dataIn = apduCommand.Serialize();

            bool debugOut = true;

            if (debugOut)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("********************************************************************************************************");
                sb.AppendLine(apduCommand.ToString());
                sb.Append("Raw Data Sent:" + Formatting.ByteArrayToHexString(dataIn));
                Logger.Log(sb.ToString());
            }

            byte[] dataOut = await CardInterfaceManger.TransmitAsync(dataIn);

            if (debugOut)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("Raw Data Received:" + Formatting.ByteArrayToHexString(dataOut));
                sb.AppendLine("********************************************************************************************************");
                Logger.Log(sb.ToString());
            }

            apduRes.Deserialize(dataOut);

            if (apduRes.SW1 == 0x61)
            {
                GetResponseRequest getResponseRequest  = new GetResponseRequest(apduRes.SW2);
                ApduResponse       getResponseResponse = await Transceive(getResponseRequest);

                if (getResponseResponse.Succeeded || (getResponseResponse.SW1 == 0x62 && getResponseResponse.SW2 == 0x83))
                {
                    apduRes.ResponseData = Formatting.ConcatArrays(apduRes.ResponseData, getResponseResponse.ResponseData, new byte[] { 0x90, 0x00 });
                    apduRes.Deserialize(apduRes.ResponseData);
                }
                else
                {
                    throw new Exception("GetResponse failed");
                }
            }
            if (apduRes.SW1 == 0x6C)
            {
                //repeat command with correct Le
                apduCommand.Le = apduRes.SW2;
                apduRes        = await Transceive(apduCommand);
            }

            return(apduRes);
        }
Exemplo n.º 7
0
        public static byte[] CalculateVISLegacyPinBlockCVN_10_18(String newPin, IKey deaKey)
        {
            byte[] block1 = Formatting.HexStringToByteArray(new String(FormatPINBlock(newPin, 0x0)));
            byte[] block2 = new byte[8];
            Array.Copy(deaKey.GetEncoded(), 4, block2, 4, 4);
            byte[] pinBlock = Formatting.Xor(block1, block2);
            byte   length   = (byte)pinBlock.Length;

            pinBlock = Formatting.ConcatArrays(new byte[] { length }, pinBlock);
            pinBlock = EMVDESSecurity.PaddingISO9797Method2(pinBlock);
            return(pinBlock);
        }
Exemplo n.º 8
0
        private static void Transmit(TCPClientStream stream, byte[] txBytes)
        {
            short length = (short)(txBytes.Length);

            byte[] lengthBytes = new byte[] { (byte)(length / 256), (byte)(length % 256) };

            byte[] txBytesTCP = Formatting.ConcatArrays(lengthBytes, txBytes);

            Logger.Log("Sending: [" + Formatting.ConvertToInt16(lengthBytes) + "]" +
                       "[" + Formatting.ByteArrayToHexString(txBytes) + "]");

            stream.Write(txBytesTCP);
        }
Exemplo n.º 9
0
 public virtual byte[] Serialize()
 {
     return(Formatting.ConcatArrays(
                new byte[] { BitConverter.GetBytes(LoadFileAID.Length / 2)[0] },
                Formatting.HexStringToByteArray(LoadFileAID),
                new byte[] { BitConverter.GetBytes(SecurityDomainAID.Length / 2)[0] },
                Formatting.HexStringToByteArray(SecurityDomainAID),
                ConvertLengthLength(LoadFileDataBlockHash.Length),
                LoadFileDataBlockHash,
                ConvertLengthLength(LoadParametersfield.Length),
                LoadParametersfield,
                ConvertLengthLength(LoadToken.Length),
                LoadToken
                ));
 }
Exemplo n.º 10
0
        private static void Transmit(TCPClientStream stream, byte[] txBytes)
        {
            byte chrSTX = 0x02; // Start of Text
            byte chrETX = 0x03; // End of Text
            //byte[] LRC;

            short length = (short)(txBytes.Length + 2);

            byte[] lengthBytes = new byte[] { (byte)(length / 256), (byte)(length % 256) };

            byte[] txBytesTCP = Formatting.ConcatArrays(lengthBytes, new byte[] { chrSTX }, txBytes, new byte[] { chrETX });

            Logger.Log("Sending: [" + Formatting.ConvertToInt16(lengthBytes) + "]" +
                       "[" + Formatting.ByteArrayToHexString(new byte[] { chrSTX }) + "]" +
                       "[" + Formatting.ByteArrayToASCIIString(txBytes) + "]" +
                       "[" + Formatting.ByteArrayToHexString(new byte[] { chrETX }) + "]");

            stream.Write(txBytesTCP);
        }
Exemplo n.º 11
0
 protected byte[] ConvertLengthLength(int length)
 {
     if (length <= 0x80)
     {
         return(new byte[] { BitConverter.GetBytes(length).Reverse().ToArray()[3] });
     }
     if (length >= 0x81 && (length - 0x81) <= 0xFF)
     {
         byte val = BitConverter.GetBytes(length - 0x81).Reverse().ToArray()[3];
         return(new byte[] { 0x81, val });
     }
     if ((length - 0x81) > 0xFF)
     {
         byte[] vals = new byte[2];
         Array.Copy(BitConverter.GetBytes(length - 0x82).Reverse().ToArray(), 2, vals, 0, 2);
         return(Formatting.ConcatArrays(new byte[] { 0x82 }, vals));
     }
     throw new Exception("Invalid length");
 }
Exemplo n.º 12
0
        public byte[] BuildStaticDataToBeAuthenticated()
        {
            TLV aip = database.Get(EMVTagsEnum.APPLICATION_INTERCHANGE_PROFILE_82_KRN.Tag);

            if (aip != null)
            {
                TLVList newList = new TLVList();
                foreach (TLV tlv in listToManage)
                {
                    if (tlv.Tag.TagLable != aip.Tag.TagLable)
                    {
                        newList.AddToList(tlv, true);
                    }
                }
                if (database.IsNotEmpty(EMVTagsEnum.STATIC_DATA_AUTHENTICATION_TAG_LIST_9F4A_KRN.Tag))
                {
                    StringBuilder sb    = new StringBuilder();
                    int           depth = 0;
                    sb.Append("Final StaticDataToBeAuthenticatedList (AIP and STATIC_DATA_AUTHENTICATION_TAG_LIST_9F4A_KRN in DB): \n");
                    sb.AppendLine(newList.ToPrintString(ref depth));
                    depth = 1;
                    sb.AppendLine(aip.ToPrintString(ref depth));
                    Logger.Log(sb.ToString());
                    return(Formatting.ConcatArrays(newList.Serialize(), aip.Value));
                }
                else
                {
                    int depth = 0;
                    Logger.Log("Final StaticDataToBeAuthenticatedList: (AIP in DB but No STATIC_DATA_AUTHENTICATION_TAG_LIST_9F4A_KRN in DB)\n" + ToPrintString(ref depth));
                    return(newList.Serialize());
                }
            }
            else
            {
                int depth = 0;
                Logger.Log("Final StaticDataToBeAuthenticatedList (No AIP in DB): \n" + ToPrintString(ref depth));
                return(Serialize());
            }
        }
Exemplo n.º 13
0
        public static byte[] BuildPinVerifyData(KernelDatabaseBase database, CAPublicKeyCertificate caPublicKey, byte[] pinBlock, byte[] challenge)
        {
            IssuerPublicKeyCertificate ipk = IssuerPublicKeyCertificate.BuildAndValidatePublicKey(database, caPublicKey.Modulus, caPublicKey.Exponent);

            if (ipk == null)
            {
                return(null);
            }

            int keyLength = 0;
            PublicKeyCertificate iccKey = IccPinKeyCertificate.BuildAndValidatePublicKey(database, ipk.Modulus, ipk.Exponent);

            if (iccKey == null)
            {
                iccKey = IccPublicKeyCertificate.BuildAndValidatePublicKey(database, database.StaticDataToBeAuthenticated, ipk.Modulus, ipk.Exponent);
                if (iccKey == null)
                {
                    return(null);
                }

                keyLength = ((IccPublicKeyCertificate)iccKey).ICCPublicKeyLength;
            }
            else
            {
                keyLength = ((IccPinKeyCertificate)iccKey).ICCPinKeyLength;
            }

            int paddingLength = keyLength - 17;

            byte[] padding = new byte[paddingLength];
            byte[] pinData = Formatting.ConcatArrays(new byte[] { 0x7F }, pinBlock, challenge, padding);

            //apply recovery function
            byte[] encryptedPin = PublicKeyCertificate.DecryptRSA(pinData, iccKey.Modulus, iccKey.Exponent);
            return(encryptedPin);
        }
Exemplo n.º 14
0
        public void OpenSecureChannel(GPPlaintextKeys keys, List <APDUMode> securityLevel, byte[] hostChallenge = null, byte[] initUpdateResponse = null, byte[] externalAuthReponse = null)
        {
            if (securityLevel.Contains(APDUMode.ENC) && !securityLevel.Contains(APDUMode.MAC))
            {
                securityLevel.Add(APDUMode.MAC);
            }

            if (hostChallenge == null)
            {
                // Generate host challenge
                hostChallenge = new byte[8];
                SecureRandom sr = new SecureRandom();
                sr.NextBytes(hostChallenge);
            }

            GPInitializeUpdateReqest initUpdate = new GPInitializeUpdateReqest(keys.GetKeysetVersion(), keys.GetKeysetID(), hostChallenge);
            //System.Diagnostics.Debug.WriteLine(initUpdate.ToPrintString());
            GPInitializeUpdateResponse response;

            if (initUpdateResponse != null)
            {
                response = new GPInitializeUpdateResponse();
                response.Deserialize(initUpdateResponse);
                System.Diagnostics.Debug.WriteLine(response.ToPrintString());
            }
            else
            {
                response = (GPInitializeUpdateResponse)SendCommand(initUpdate);
            }


            // Detect and report locked cards in a more sensible way.
            if ((response.SW == (ushort)ISO7816ReturnCodes.SW_SECURITY_STATUS_NOT_SATISFIED) || (response.SW == (ushort)ISO7816ReturnCodes.SW_AUTHENTICATION_METHOD_BLOCKED))
            {
                throw new Exception("INITIALIZE UPDATE failed, Card possibly locked.");
            }

            if (response.SW != (ushort)ISO7816ReturnCodes.SW_NO_ERROR)
            {
                throw new Exception("INITIALIZE UPDATE failed");
            }

            // Verify response length (SCP01/SCP02 + SCP03 + SCP03 w/ pseudorandom)
            if (response.ResponseData.Length != 28 && response.ResponseData.Length != 29 && response.ResponseData.Length != 32)
            {
                throw new Exception("Invalid INITIALIZE UPDATE response length: " + response.ResponseData.Length);
            }

            System.Diagnostics.Debug.WriteLine("Host challenge: " + Formatting.ByteArrayToHexString(hostChallenge));
            System.Diagnostics.Debug.WriteLine("Card challenge: " + Formatting.ByteArrayToHexString(response.CardChallenge));

            // Verify response
            // If using explicit key version, it must match.
            if ((keys.GetKeysetVersion() > 0) && (response.KeyVersionNumber != keys.GetKeysetVersion()))
            {
                throw new Exception("Key version mismatch: " + keys.GetKeysetVersion() + " != " + response.KeyVersionNumber);
            }

            System.Diagnostics.Debug.WriteLine("Card reports SCP0" + response.SCPId + " with version " + response.KeyVersionNumber + " keys");
            if (response.SCPId == 3)
            {
                System.Diagnostics.Debug.WriteLine("SCP03 i=" + response.SCPI);
            }

            SCPVersions scpVersion;
            // Derive session keys
            GPKeySet sessionKeys;

            if (response.SCPId == 1)
            {
                if (securityLevel.Contains(APDUMode.RMAC))
                {
                    throw new Exception("SCP01 does not support RMAC");
                }

                scpVersion  = SCPVersions.SCP_01_05;
                sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, hostChallenge, response.CardChallenge);
            }
            else if (response.SCPId == 2)
            {
                scpVersion  = SCPVersions.SCP_02_15;
                sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, response.CardChallengeSeq);
            }
            else if (response.SCPId == 3)
            {
                scpVersion  = SCPVersions.SCP_03;
                sessionKeys = keys.GetSessionKeys(response.SCPId, response.KeyDiversificationData, hostChallenge, response.CardChallenge);
            }
            else
            {
                throw new Exception("Unsupported scpVersion: " + response.SCPId);
            }

            // Verify card cryptogram
            byte[] my_card_cryptogram = null;
            byte[] cntx;
            if (response.SCPId == 2)
            {
                cntx = Formatting.ConcatArrays(hostChallenge, response.CardChallengeSeq, response.CardChallenge);
            }
            else
            {
                cntx = Arrays.Concatenate(hostChallenge, response.CardChallenge);
            }

            if (response.SCPId == 1 || response.SCPId == 2)
            {
                my_card_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), cntx);
            }
            else
            {
                my_card_cryptogram = SCP03Wrapper.Scp03_kdf(sessionKeys.GetKey(KeySessionType.MAC), (byte)0x00, cntx, 64);
            }

            // This is the main check for possible successful authentication.
            if (!Arrays.AreEqual(response.CardCryptogram, my_card_cryptogram))
            {
                string message = "Card cryptogram invalid." +
                                 "\nCard: " + Formatting.ByteArrayToHexString(response.CardCryptogram) +
                                 "\nCalculated: " + Formatting.ByteArrayToHexString(my_card_cryptogram) +
                                 "\nRetrying the same parameters may disable the card";

                System.Diagnostics.Debug.WriteLine(message);
                throw new Exception(message);
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Verified card cryptogram: " + Formatting.ByteArrayToHexString(my_card_cryptogram));
            }

            // Calculate host cryptogram and initialize SCP wrapper
            byte[] host_cryptogram = null;
            if (response.SCPId == 1 || response.SCPId == 2)
            {
                if (response.SCPId == 2)
                {
                    host_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), Formatting.ConcatArrays(response.CardChallengeSeq, response.CardChallenge, hostChallenge));
                }
                else
                {
                    host_cryptogram = SCP0102Wrapper.Mac_3des_nulliv(sessionKeys.GetKey(KeySessionType.ENC), Arrays.Concatenate(response.CardChallenge, hostChallenge));
                }
                wrapper = new SCP0102Wrapper(sessionKeys, scpVersion, new List <APDUMode>()
                {
                    APDUMode.MAC
                }, null, null, blockSize);
            }
            else
            {
                host_cryptogram = SCP03Wrapper.Scp03_kdf(sessionKeys.GetKey(KeySessionType.MAC), (byte)0x01, cntx, 64);
                wrapper         = new SCP03Wrapper(sessionKeys, scpVersion, new List <APDUMode>()
                {
                    APDUMode.MAC
                }, null, null, blockSize);
            }
            System.Diagnostics.Debug.WriteLine("Calculated host cryptogram: " + Formatting.ByteArrayToHexString(host_cryptogram));

            GPExternalAuthenticateReqest   externalAuthenticate = new GPExternalAuthenticateReqest(GetSetValue(securityLevel), host_cryptogram);
            GPExternalAuthenticateResponse response2;

            if (externalAuthReponse != null)
            {
                response2 = new GPExternalAuthenticateResponse();
                response2.Deserialize(externalAuthReponse);
                System.Diagnostics.Debug.WriteLine(response2.ToPrintString());
            }
            else
            {
                response2 = (GPExternalAuthenticateResponse)SendCommand(externalAuthenticate);
            }


            if (response2.SW != (ushort)ISO7816ReturnCodes.SW_NO_ERROR)
            {
                throw new Exception("External authenticate failed");
            }

            wrapper.SetSecurityLevel(securityLevel);
        }
Exemplo n.º 15
0
        internal static IssuerPublicKeyCertificate BuildAndValidatePublicKey(KernelDatabaseBase database, byte[] caPublicKeyModulus, byte[] caPublicKeyExponent)
        {
            //section 6.3 EMV 4.3 Book 2

            TLV issuerPublicKeyCertificate = database.Get(EMVTagsEnum.ISSUER_PUBLIC_KEY_CERTIFICATE_90_KRN);
            TLV issuerPublicKeyExponent    = database.Get(EMVTagsEnum.ISSUER_PUBLIC_KEY_EXPONENT_9F32_KRN);
            TLV issuerPublicKeyRemainder   = database.Get(EMVTagsEnum.ISSUER_PUBLIC_KEY_REMAINDER_92_KRN);

            if (issuerPublicKeyCertificate.Value.Length != caPublicKeyModulus.Length)
            {
                return(null);
            }

            byte[] decrypt = DecryptRSA(issuerPublicKeyCertificate.Value, caPublicKeyModulus, caPublicKeyExponent);

            IssuerPublicKeyCertificate issuerCertData = new IssuerPublicKeyCertificate(decrypt, caPublicKeyModulus.Length,
                                                                                       issuerPublicKeyRemainder == null ? new byte[0] : issuerPublicKeyRemainder.Value, issuerPublicKeyExponent.Value);

            if (issuerCertData.RecoveredDataTrailer != 0xBC)
            {
                return(null);
            }

            if (issuerCertData.RecoveredDataHeader != 0x6A)
            {
                return(null);
            }

            if (issuerCertData.CertificateFormat != 0x02)
            {
                return(null);
            }

            if (!issuerCertData.ValidateHash())
            {
                return(null);
            }

            string pan = Formatting.ByteArrayToHexString(database.Get(EMVTagsEnum.APPLICATION_PRIMARY_ACCOUNT_NUMBER_PAN_5A_KRN).Value);
            string issuerIdentifier = Formatting.ByteArrayToHexString(issuerCertData.IssuerIdentifier).Replace("FF", "");

            if (!pan.StartsWith(issuerIdentifier))
            {
                return(null);
            }

            DateTime expiry = DateTime.ParseExact(Formatting.BcdToString(issuerCertData.ExpiryDate), "MMyy", System.Globalization.CultureInfo.InvariantCulture);

            //TODO: if you have a test tool trying to use an expired cert then comment this test out or update your test tool
            //if (expiry <= DateTime.Now)
            //{
            //    Logger.Log("Error: Trying to use an expired issuer public key");
            //    return null;
            //}

            //step 10 optional

            if (issuerCertData.PublicKeyAlgorithmIndicator != 0x01)
            {
                return(null);
            }

            if (issuerPublicKeyRemainder != null)
            {
                issuerCertData.Modulus = Formatting.ConcatArrays(issuerCertData.UnpaddedIssuerPublicKeyorLeftmostDigitsofIssuerPublicKey, issuerPublicKeyRemainder.Value);
            }
            else
            {
                issuerCertData.Modulus = issuerCertData.UnpaddedIssuerPublicKeyorLeftmostDigitsofIssuerPublicKey;
            }

            return(issuerCertData);
        }
Exemplo n.º 16
0
        public static byte[] DecryptRSA(byte[] bytesToDecrypt, byte[] publicKeyModulus, byte[] publicKeyExponent)
        {
            //Prepend 0x00 to unsigned data to avoid that the most significant bit is interpreted as the "signed" bit
            byte[] modAppend;
            byte[] expAppend;
            byte[] dataAppend;

            if (publicKeyModulus[0] >= 0x80)
            {
                modAppend = Formatting.ConcatArrays(new byte[] { 0x00 }, publicKeyModulus);
            }
            else
            {
                modAppend = publicKeyModulus;
            }

            if (publicKeyExponent[0] >= 0x80)
            {
                expAppend = Formatting.ConcatArrays(new byte[] { 0x00 }, publicKeyExponent);
            }
            else
            {
                expAppend = publicKeyExponent.Reverse().ToArray();
            }

            if (bytesToDecrypt[0] >= 0x80)
            {
                dataAppend = Formatting.ConcatArrays(new byte[] { 0x00 }, bytesToDecrypt);
            }
            else
            {
                dataAppend = bytesToDecrypt;
            }

            //Bouncy Castle BigInteger expects array to be in big endian order, as per EMV data, if this were a .NET BigInteger is would expect it in little endian
            BigInteger biMod = new BigInteger(1, modAppend);
            BigInteger biExp = new BigInteger(1, expAppend);
            BigInteger biDta = new BigInteger(1, dataAppend);

            byte[] result = biDta.ModPow(biExp, biMod).ToByteArray();

            //these seems to be a bug in the bouncy castle RsaCoreEngine ConvertInput method, we add 0x00 to avoid misinterpretation of our numbers as -,
            //since they are unsigned, and BigInteger expects a signed number, this adding of the byte makes the length of the input longer and
            //the following lines of code => int maxLength = (bitSizeOfModulas + 7) / 8; if (inLen > maxLength) <= compares the length of the Modulus after
            //BigInteger conversion to the length of the input array before BigInteger confusion, resulting in different lengths

            //RsaKeyParameters keyParameter = new RsaKeyParameters(false, biMod, biExp);
            //Pkcs1Encoding encryptEngine = new Pkcs1Encoding(new RsaEngine());
            //encryptEngine.Init(false, keyParameter);
            //byte[] decryptedBytes = encryptEngine.ProcessBlock(dataAppend, 0, dataAppend.Length);

            if (result.Length == (bytesToDecrypt.Length + 1) && result[0] == (byte)0x00)
            {
                //Remove 0x00 from beginning of array
                byte[] tmp = new byte[bytesToDecrypt.Length];
                Array.Copy(result, 1, tmp, 0, bytesToDecrypt.Length);
                result = tmp;
            }

            return(result);
        }
Exemplo n.º 17
0
        internal static IccPublicKeyCertificate BuildAndValidatePublicKey(KernelDatabaseBase database, StaticDataToBeAuthenticatedList staticDataToBeAuthenticated, byte[] issuerPublicKeyModulus, byte[] issuerPublicKeyExponent)
        {
            //section 6.4 EMV 4.3 Book 2

            TLV iccPublicKeyCertificate = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PUBLIC_KEY_CERTIFICATE_9F46_KRN);
            TLV iccPublicKeyExponent    = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PUBLIC_KEY_EXPONENT_9F47_KRN);
            TLV iccPublicKeyRemainder   = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PUBLIC_KEY_REMAINDER_9F48_KRN);

            if (iccPublicKeyCertificate.Value.Length != issuerPublicKeyModulus.Length)
            {
                return(null);
            }

            byte[] decrypt = DecryptRSA(iccPublicKeyCertificate.Value, issuerPublicKeyModulus, issuerPublicKeyExponent);

            IccPublicKeyCertificate iccCertData = new IccPublicKeyCertificate(decrypt, issuerPublicKeyModulus.Length,
                                                                              iccPublicKeyRemainder == null ? new byte[0] : iccPublicKeyRemainder.Value, iccPublicKeyExponent.Value, database.StaticDataToBeAuthenticated.BuildStaticDataToBeAuthenticated());

            if (iccCertData.RecoveredDataTrailer != 0xBC)
            {
                return(null);
            }

            if (iccCertData.RecoveredDataHeader != 0x6A)
            {
                return(null);
            }

            if (iccCertData.CertificateFormat != 0x04)
            {
                return(null);
            }

            if (!iccCertData.ValidateHash())
            {
                return(null);
            }

            string pan          = Formatting.ByteArrayToHexString(database.Get(EMVTagsEnum.APPLICATION_PRIMARY_ACCOUNT_NUMBER_PAN_5A_KRN).Value);
            string panToCompare = Formatting.ByteArrayToHexString(iccCertData.ApplicationPAN).Replace("FF", "");

            if (!pan.StartsWith(panToCompare))
            {
                return(null);
            }

            DateTime expiry = DateTime.ParseExact(Formatting.BcdToString(iccCertData.ExpiryDate), "MMyy", System.Globalization.CultureInfo.InvariantCulture);

            //TODO: if you have a test tool trying to use an expired cert then comment this test out or update your test tool
            //if (expiry <= DateTime.Now)
            //{
            //    Logger.Log("Error: Trying to use an expired issuer public key");
            //    return null;
            //}

            if (iccCertData.PublicKeyAlgorithmIndicator != 0x01)
            {
                return(null);
            }

            if (iccPublicKeyRemainder != null)
            {
                iccCertData.Modulus = Formatting.ConcatArrays(iccCertData.UnpaddedICCPublicKeyorLeftmostDigitsofIssuerPublicKey, iccPublicKeyRemainder.Value);
            }
            else
            {
                iccCertData.Modulus = iccCertData.UnpaddedICCPublicKeyorLeftmostDigitsofIssuerPublicKey;
            }

            return(iccCertData);
        }
Exemplo n.º 18
0
        private byte[] DoGPO(byte[] adpu)
        {
            TLVList db = new TLVList();

            EMVGetProcessingOptionsRequest request = new EMVGetProcessingOptionsRequest();

            request.Deserialize(adpu);

            TLV _83 = TLV.Create(EMVTagsEnum.COMMAND_TEMPLATE_83_KRN.Tag);

            _83.Deserialize(request.CommandData, 0);

            int pos = 0;

            byte[] amount = Formatting.copyOfRange(_83.Value, pos, pos + MAX_AMOUNT_AUTH__LENGTH);
            pos = pos + MAX_AMOUNT_AUTH__LENGTH;
            byte[] upn = Formatting.copyOfRange(_83.Value, pos, pos + MAX_UNPRED_NUM__LENGTH);
            pos = pos + MAX_UNPRED_NUM__LENGTH;
            byte[] ttq = Formatting.copyOfRange(_83.Value, pos, pos + MAX_TTQ_LENGTH);
            //pos = pos + MAX_TTQ_LENGTH;

            db.AddToList(TLV.Create(EMVTagsEnum.TERMINAL_TRANSACTION_QUALIFIERS_TTQ_9F66_KRN.Tag, ttq));

            /*
             * supported by card and reader (TTQ byte 1 bit 6 set to 1b)
             * return 6985 if not
             */
            TERMINAL_TRANSACTION_QUALIFIERS_9F66_KRN ttqST = new TERMINAL_TRANSACTION_QUALIFIERS_9F66_KRN(db.Get(EMVTagsEnum.TERMINAL_TRANSACTION_QUALIFIERS_TTQ_9F66_KRN.Tag));

            if (!ttqST.Value.EMVModeSupported)
            {
                return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_CONDITIONS_OF_USE_NOT_SATISFIED));
            }

            /*
             * Card action analysis
             */

            /*
             * Card Risk Management Processing
             */

            /*
             * Initialize data
             */
            //Req H.2 (Initialization of Card Transaction Qualifiers)
            //The card shall reset CTQ byte 1 bits 8-7 to 00b (indicating Online PIN Not Required and Signature Not Required).
            CARD_TRANSACTION_QUALIFIERS_CTQ_9F6C_KRN3 ctq = new CARD_TRANSACTION_QUALIFIERS_CTQ_9F6C_KRN3();

            ctq.Value.OnlinePINRequired = false;
            ctq.Value.SignatureRequired = false;

            ctq.Value.GoOnlineIfApplicationExpired = true;                                     //hardcoded perso
            ctq.Value.GoOnlineIfOfflineDataAuthenticationFailsAndReaderIsOnlineCapable = true; //hardcoded perso

            //Req H.3 (Initialization of Issuer Application Data)
            //The card shall set the CVR to '03 80 00 00' (indicating Second GENERATE AC not requested)
            byte[] cvr = new byte[4];
            cvr[Byte1] = 0x03;
            cvr[Byte2] = (byte)0x80;
            cvr[Byte3] = 0x00;
            cvr[Byte4] = 0x00;

            //Req H.4 (Initialization of Cryptogram Information Data)
            //The card shall reset the Cryptogram Information Data (CID) to '00'.
            byte[] cid_9F27 = new byte[1];
            cid_9F27[Byte1] = 0x00;

            /*
             * Application Block Check
             */
            //Req H.5 (Application Blocked Check)
            //If the application is blocked, then the card shall discontinue processing the command and shall respond with SW1 SW2 = '6985'

            /*
             * PIN tries exceeded check
             */

            /*
             * Refunds and Credits Check
             */

            /*
             * Reader Indicators Check
             */

            /*
             * Cardholder Verification Method Check
             */

            //Req H.6 (CVM Required Check)
            //If CVM Required by reader (TTQ byte 2 bit 7 is 1b), then a CVM is required for the
            //transaction, and the card shall determine the common CVM to be performed
            byte[] capDefault = PersoAndCardStateStorage.CARD_ADDITIONAL_PROCESSES_9F68_KRN.Value;
            if (ttqST.Value.CVMRequired)
            {
                //Req H.7 (Determine Common CVM)
                //If a CVM is required for the transaction, then the card shall attempt to select a
                //common CVM supported by both itself and the reader, as defined in this requirement.
                //If there is more than one CVM supported by both the card and the reader, the
                //selected CVM is chosen based on the following defined CVM hierarchy: 1) Online
                //PIN, 2) Signature.
                if (isCapPersonalized)
                {
                    //If CVM Required by reader (TTQ byte 2 bit 7 is 1b), then a CVM is required for the
                    //transaction, and the card shall determine the common CVM to be performed.
                    if (ttqST.Value.CVMRequired)
                    {
                        //Online PIN supported by reader (TTQ byte 1 bit 3 is 1b) and either
                        //Online PIN supported by card for domestic transactions (CAP byte 3 bit 8 is 1b)
                        //or Online PIN supported by card for international transactions (CAP byte 3 bit 7 is 1b)
                        if (ttqST.Value.OnlinePINSupported && Formatting.GetBitPosition(capDefault[Byte3], Bit8 + 1) || Formatting.GetBitPosition(capDefault[Byte3], Bit7 + 1))
                        {
                            //Then the card shall indicate Online PIN Required (set CTQ byte 1 bit 8 to 1b)
                            ctq.Value.OnlinePINRequired = true;
                            //Else, if both of the following are true
                        }
                        else if (ttqST.Value.OfflineDataAuthenticationForOnlineAuthorizationsSupported && Formatting.GetBitPosition(capDefault[Byte3], Bit5 + 1))
                        {
                            ctq.Value.GoOnlineIfOfflineDataAuthenticationFailsAndReaderIsOnlineCapable = true;
                        }
                        else
                        {
                            return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_DATA_INVALID));
                        }
                    }
                }
                else
                {
                    //if Signature is not supported by the reader (TTQ byte 1 bit 2 is 0b), then the card
                    //shall discontinue processing and respond to the GPO command with SW1 SW2 = '6984'
                    if (!ttqST.Value.SignatureSupported)
                    {
                        return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_DATA_INVALID));
                    }
                }
            }

            /*
             * Domestic Velocity Checking
             */

            /*
             * International Velocity Checking
             */

            /*
             * Contactless Transaction Counter Velocity Checking
             */

            /*
             * Transaction Disposition
             */
            //Req H.8 (Online)
            //Indicate Authorization Request Cryptogram returned (set CVR byte 2 bits 6-5
            //to 10b and set CID bits 8-7 to 10b).
            Formatting.SetBitPosition(ref cvr[Byte2], true, Bit6 + 1);
            Formatting.SetBitPosition(ref cvr[Byte2], false, Bit5 + 1);
            Formatting.SetBitPosition(ref cid_9F27[Byte1], true, Bit8 + 1);
            Formatting.SetBitPosition(ref cid_9F27[Byte1], false, Bit7 + 1);

            //Increment the Application Transaction Counter (ATC) by one. The ATC shall be
            //incremented prior to the performance of any cryptographic operations.
            //If incrementing the ATC results in the ATC reaching its maximum value, then the
            //application shall be permanently blocked, Req 6.7 (Application Permanently
            //Blocked), and shall respond to the GPO command with error SW1 SW2 = '6985'

            //Increment the Application Transaction Counter (ATC) by one. The ATC shall be
            //incremented prior to the performance of any cryptographic operations
            TLV atc_9F36 = PersoAndCardStateStorage.APPLICATION_TRANSACTION_COUNTER_ATC_9F36_KRN;

            if (atc_9F36.Value[0] == 0xFF)
            {
                return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_CONDITIONS_OF_USE_NOT_SATISFIED));
            }
            else
            {
                if (atc_9F36.Value[1] == 0xFF)
                {
                    atc_9F36.Value[0] = (byte)(atc_9F36.Value[0] + 1);
                    atc_9F36.Value[1] = 0x00;
                }
                else
                {
                    atc_9F36.Value[1] = (byte)(atc_9F36.Value[1] + 1);
                }
            }

            //Construct the Issuer Application Data. If an Issuer Discretionary Data Option (IDD
            //Option) is supported (see Appendix E), it shall be constructed and the MAC
            //generated (if applicable).
            //Only Option 0 supported, IDD already included during perso

            //If the card is capable of performing fDDA and all of the following are true:
            //the card supports fDDA for Online Authorizations (AIP byte 1 bit 6 is 1b for the
            //"Online (with ODA)" GPO response)
            //and ODA for Online Authorizations supported by card (CAP byte 2 bit 6 is 0b)
            //and ODA for Online Authorizations supported by reader (TTQ byte 1 bit 1 is 1b)
            //Then the card shall construct the Card Authentication Related Data and generate
            //the Signed Dynamic Application Data (tag '9F4B'). The Signed Dynamic
            //Application Data shall be generated as defined in Appendix A.
            byte[] iapDefault = PersoAndCardStateStorage.APPLICATION_INTERCHANGE_PROFILE_82_KRN.Value;
            if (Formatting.GetBitPosition(iapDefault[Byte1], Bit6 + 1) && !Formatting.GetBitPosition(capDefault[Byte2], Bit6 + 1) &&
                ttqST.Value.OfflineDataAuthenticationForOnlineAuthorizationsSupported)
            {
                //fdda not supported
                return(BitConverter.GetBytes((int)ISO7816ReturnCodes.SW_CONDITIONS_OF_USE_NOT_SATISFIED));
            }

            //Generate the Application Cryptogram
            //The path shall implement support for Cryptogram Version Number 10 and
            //Cryptogram Version Number 17, and may implement support for Cryptogram Version
            //Number 18 at implementer discretion. The Cryptogram Version to be used for
            //cryptogram generation shall be issuer configurable, and indicated by the issuer in the
            //Cryptogram Version Number of the Issuer Application Data returned for
            //transactions.
            //We use only 17 at this stage
            //if(tm_CVN[Byte1] != 0x11)
            //    ISOException.throwIt((short) 0x6984);

            //9F10 IAD Byte 5 (IAD byte 5 is CVR byte 2) from card
            TLV iad_9F10 = PersoAndCardStateStorage.ISSUER_APPLICATION_DATA_9F10_KRN;

            iad_9F10.Value[Byte5] = cvr[Byte2];

            //9F02 Amount, Authorized from terminal via pdol
            //9F37 Unpredictable Number from terminal via pdol
            //9F36 ATC from card
            //9F10 IAD Byte 5 (IAD byte 5 is CVR byte 2) from card
            byte[] crytogram_9F26 = generateCryptogram17(PersoAndCardStateStorage.ICC_MK,
                                                         Formatting.ConcatArrays(amount, upn, atc_9F36.Value, new byte[] { iad_9F10.Value[Byte5] }));

            //If ODA for Online Authorizations supported by reader (TTQ byte 1 bit 1 is 1b), then
            //construct and send the GPO response in [EMV] Format 2 with the data shown in
            //Table 6-2 using Condition column "Online (with ODA)".
            EMVGetProcessingOptionsResponse response;

            //pdol sent back during select is 9F38 09 -> 9F02 06 9F37 04 9F66 04
            ctq.Serialize();
            db.AddToList(TLV.Create(EMVTagsEnum.AMOUNT_AUTHORISED_NUMERIC_9F02_KRN.Tag, amount));
            db.AddToList(TLV.Create(EMVTagsEnum.UNPREDICTABLE_NUMBER_9F37_KRN.Tag, upn));
            db.AddToList(TLV.Create(EMVTagsEnum.TERMINAL_TRANSACTION_QUALIFIERS_TTQ_9F66_KRN.Tag, ttq));
            db.AddToList(TLV.Create(EMVTagsEnum.CARD_TRANSACTION_QUALIFIERS_CTQ_9F6C_KRN3.Tag, ctq.Value.Value));
            db.AddToList(TLV.Create(EMVTagsEnum.CRYPTOGRAM_INFORMATION_DATA_9F27_KRN.Tag, cid_9F27));
            db.AddToList(TLV.Create(EMVTagsEnum.APPLICATION_CRYPTOGRAM_9F26_KRN.Tag, crytogram_9F26));
            TLV sdad = TLV.Create(EMVTagsEnum.SIGNED_DYNAMIC_APPLICATION_DATA_9F4B_KRN.Tag);

            sdad.Val.PackValue(15);
            db.AddToList(sdad);
            db.AddToList(atc_9F36);
            db.AddToList(iad_9F10);

            if (ttqST.Value.OfflineDataAuthenticationForOnlineAuthorizationsSupported)
            {
                //only diff is to add empty 9F4B SDAD
                response = genGPOResponse(true, db);
            }
            //Else (TTQ byte 1 bit 1 is 0b), construct and send the GPO response in [EMV]
            //Format 2 with the data shown in Table 6-2 using Condition column "Online and
            //Decline (without ODA)".
            else
            {
                response = genGPOResponse(false, db);
            }
            //Note: The Available Offline Spending Amount (tag '9F5D') is not supported

            return(response.Serialize());
        }
        private ApproverResponseBase DoEMVAuth(ApproverRequestBase requestIn)
        {
            EMVApproverRequest request = ((EMVApproverRequest)requestIn);

            CryptoMetaData cryptoMetaData = EMVDESSecurity.BuildCryptoMeta(request.EMV_Data);

            //Do additional checking here, e.g. customer balances etc
            //if decline set isApproved to false
            bool isApproved = true;
            //do we want to send back a pin change script,
            string newPin = "";// = "4315";
            //decide whether to send 71 or 72 script template, 71 scripts applied before 2nd gen ac , 72 scripts applied after 2nd gen ac
            bool doPinChangeBefore = false;

            TLV    _8A;
            string responseMessage;

            if (isApproved)
            {
                _8A             = TLV.Create(EMVTagsEnum.AUTHORISATION_RESPONSE_CODE_8A_KRN.Tag, arcApproved);
                responseMessage = "Approved";
            }
            else
            {
                _8A             = TLV.Create(EMVTagsEnum.AUTHORISATION_RESPONSE_CODE_8A_KRN.Tag, arcDeclined);
                responseMessage = "Declined";
            }

            TLV _91;

            byte[] arpc;
            //returns null if arqc cannot be verified
            if (cryptoMetaData.CryptoVersion == CrptoVersionEnum._18)
            {
                arpc = EMVDESSecurity.VerifyCryptogramGenARPC(request.EMV_Data, cryptoMetaData, PackCSU());
            }
            else
            {
                arpc = EMVDESSecurity.VerifyCryptogramGenARPC(request.EMV_Data, cryptoMetaData, _8A.Value);
            }

            if (arpc != null)
            {
                _91 = Pack91(cryptoMetaData, arpc, _8A);// TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, Formatting.ConcatArrays(arpc, _8A.Value));
                Logger.Log("Tx approved: " + isApproved + " ARQC passed, ARPC is " + Formatting.ByteArrayToHexString(arpc));
            }
            else
            {
                isApproved      = false;
                responseMessage = "Tx Declined: ARQC Failure";
                _8A             = TLV.Create(EMVTagsEnum.AUTHORISATION_RESPONSE_CODE_8A_KRN.Tag, new byte[] { 0x20, 0x20 });
                _91             = Pack91(cryptoMetaData, arpc, _8A); //TLV.Create(EMVTagsEnum.ISSUER_AUTHENTICATION_DATA_91_KRN.Tag, new byte[8]);
                Logger.Log("ARQC failed");
            }

            byte[] _86 = new byte[0];
            //don't allow pin change if arqc could not be validated
            if (!string.IsNullOrWhiteSpace(newPin) && arpc != null)
            {
                try
                {
                    TLV _9F26 = request.EMV_Data.Children.Get(EMVTagsEnum.APPLICATION_CRYPTOGRAM_9F26_KRN.Tag);
                    if (_9F26 == null)
                    {
                        throw new Exception("No Cryptogram found");
                    }
                    //TODO: for mchip we must increment the arqc by one for each subsequent command created
                    _86 = EMVDESSecurity.CalculatePinChangeScript(request.EMV_Data, cryptoMetaData, newPin, _9F26.Value);
                }
                catch
                {
                    _86 = new byte[0];
                }
            }

            TLV _71TLV;
            TLV _72TLV;

            if (doPinChangeBefore)
            {
                _71TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_1_71_KRN.Tag);
                _71TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x71, (byte)_86.Length }, _86), 0);

                _72TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_2_72_KRN.Tag);
                _72TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x72, 0x00 }, new byte[0]), 0);
            }
            else
            {
                _72TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_2_72_KRN.Tag);
                _72TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x72, (byte)_86.Length }, _86), 0);

                _71TLV = TLV.Create(EMVTagsEnum.ISSUER_SCRIPT_TEMPLATE_1_71_KRN.Tag);
                _71TLV.Deserialize(Formatting.ConcatArrays(new byte[] { 0x71, 0x00 }, new byte[0]), 0);
            }

            return(new EMVApproverResponse()
            {
                IsApproved = isApproved,
                ResponseMessage = responseMessage,
                AuthCode_8A = _8A,
                IssuerAuthData_91 = _91,
                IssuerScriptTemplate_72 = _72TLV,
                IssuerScriptTemplate_71 = _71TLV,
            });
        }
Exemplo n.º 20
0
        internal static IccPinKeyCertificate BuildAndValidatePublicKey(KernelDatabaseBase database, byte[] issuerPublicKeyModulus, byte[] issuerPublicKeyExponent)
        {
            TLV iccPinKeyCertificate = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_CERTIFICATE_9F2D_KRN);

            if (iccPinKeyCertificate == null)
            {
                return(null);
            }
            TLV iccPinKeyExponent = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_EXPONENT_9F2E_KRN);

            if (iccPinKeyExponent == null)
            {
                return(null);
            }
            TLV iccPinKeyRemainder = database.Get(EMVTagsEnum.INTEGRATED_CIRCUIT_CARD_ICC_PIN_ENCIPHERMENT_PUBLIC_KEY_REMAINDER_9F2F_KRN);

            if (iccPinKeyRemainder == null)
            {
                return(null);
            }

            if (iccPinKeyCertificate.Value.Length != issuerPublicKeyModulus.Length)
            {
                return(null);
            }

            byte[] decrypt = DecryptRSA(iccPinKeyCertificate.Value, issuerPublicKeyModulus, issuerPublicKeyExponent);

            IccPinKeyCertificate iccCertData = new IccPinKeyCertificate(decrypt, issuerPublicKeyModulus.Length,
                                                                        iccPinKeyRemainder == null ? new byte[0] : iccPinKeyRemainder.Value, iccPinKeyExponent.Value);

            if (iccCertData.RecoveredDataTrailer != 0xBC)
            {
                return(null);
            }

            if (iccCertData.RecoveredDataHeader != 0x6A)
            {
                return(null);
            }

            if (iccCertData.CertificateFormat != 0x04)
            {
                return(null);
            }

            if (!iccCertData.ValidateHash())
            {
                return(null);
            }

            string pan          = Formatting.ByteArrayToHexString(database.Get(EMVTagsEnum.APPLICATION_PRIMARY_ACCOUNT_NUMBER_PAN_5A_KRN).Value);
            string panToCompare = Formatting.ByteArrayToHexString(iccCertData.ApplicationPAN).Replace("FF", "");

            if (!pan.StartsWith(panToCompare))
            {
                return(null);
            }

            DateTime expiry = DateTime.ParseExact(Formatting.BcdToString(iccCertData.ExpiryDate), "MMyy", System.Globalization.CultureInfo.InvariantCulture);

            if (expiry <= DateTime.Now)
            {
                return(null);
            }

            if (iccCertData.PublicKeyAlgorithmIndicator != 0x01)
            {
                return(null);
            }

            if (iccPinKeyRemainder != null)
            {
                iccCertData.Modulus = Formatting.ConcatArrays(iccCertData.UnpaddedICCPinKeyorLeftmostDigitsofIssuerPublicKey, iccPinKeyRemainder.Value);
            }
            else
            {
                iccCertData.Modulus = iccCertData.UnpaddedICCPinKeyorLeftmostDigitsofIssuerPublicKey;
            }

            return(iccCertData);
        }