PACE PIN 建立后的 SM 请求

问题描述 投票:0回答:1

根据 ICAO Doc 9303 第 11 部分设置 PACE 身份验证流程(PACE - GENERIC MAPPING(信息))后,我能够在芯片和终端之间交换令牌(请参阅上一个问题:CMac 计算)在 C# 中使用 BouncyCastle)。 我依靠 PlatinumReader 智能卡读卡器的日志,尝试从读取模式下受保护的数据组中读取数据(访问条件 10FFFF)。

一旦我从芯片收到令牌,我应该如何处理它?

这是PlatinumReader建立PACE认证后的日志:终端和芯片交换令牌

Key: T_PCD - PCD's calculated authentication token (T_PCD [PACE])
**3e 93 48 fc ca 2c 5d dc**
Key: T_PICC - PICC's calculated authentication token (T_PICC [PACE])
8b 9e 28 f9 81 be 50 86 
APDU: GeneralAuthenticate - Start
APDU: Request
00 86 00 00 0c 7c 0a 85 08 3e 93 48 fc ca 2c 5d dc 00
APDU: Response
7c 0a 86 08 8b 9e 28 f9 81 be 50 86 90 00
APDU: GeneralAuthenticate - End
Protocol: PACE - End
PACE processing time 00:00:00.296

之后,终端能够读取受保护的DG4文件(访问条件10FFFF)

Reading file DG4 in application ePassport
ReadData: ePass.DG4 - Start
APDU: Select File - Start
APDU: Request
00 a4 02 0c 02 01 04
 APDU: SMRequest - 000000000000000000000000000001F1
0c a4 02 0c 1d 87 11 01 dd f3 54 5a bd f6 5a 3c
4b 8a ec c6 50 c2 48 2b 8e 08 ea d4 db 01 00 1a
ee 9a 00                                        ...
APDU: SMResponse - 000000000000000000000000000001F2
99 02 90 00 8e 08 12 25 06 c5 82 f8 37 23 90 00
APDU: Response
90 00

这是我的PACE认证功能,我可以与芯片交换令牌:

 public void paceGenericMapping()
  {
      string selectFile1 = executeCommande("00a4020c020104");
      string readData2 = executeCommande("00b0000005");

      // Paramètres du domaine pour la courbe ECC-NIST 256
      X9ECParameters curve = SecNamedCurves.GetByName("secp256r1");
      // Utilisation des paramètres déjà connus

      Org.BouncyCastle.Math.BigInteger prime = new Org.BouncyCastle.Math.BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16);
      Org.BouncyCastle.Math.BigInteger a = new Org.BouncyCastle.Math.BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16);
      Org.BouncyCastle.Math.BigInteger order = new Org.BouncyCastle.Math.BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16);
      Org.BouncyCastle.Math.BigInteger b = new Org.BouncyCastle.Math.BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", 16);
      Org.BouncyCastle.Math.BigInteger G_X = new Org.BouncyCastle.Math.BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16);
      Org.BouncyCastle.Math.BigInteger G_Y = new Org.BouncyCastle.Math.BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16);
      Org.BouncyCastle.Math.EC.ECPoint basePoint = curve.Curve.CreatePoint(G_X, G_Y);


      ECDomainParameters domainParams = new ECDomainParameters(curve.Curve, basePoint, order, curve.H, curve.GetSeed());

      //  • Read EF.DIR and EF.CardAccess (applet capabilities)
      string selectEF_F000 = executeCommande("00A40000023F00");
      string selectEF_CardAccess = executeCommande("00a4020c02011c");
      string read_binary_1 = executeCommande("00b0000005");
      string read_binary_2 = executeCommande("00b000055b");

      //  • Select algorithm (INS 22)
      string setMSE = executeCommande("0022c1a412800a04007f0007020204020283010384010c");

      //  • The host requests PACE for authentication, possibly giving a preference : 
      string paceAuthenticationREQUEST = executeCommande("10860000027c0000");

      //  •   The card responds with a 128-bit (16 byte) random number (nonce) encrypted with PACE_nonce_AES128key  : 7C 12 80 10 <encrypted_nonce>
      int startIndex = paceAuthenticationREQUEST.IndexOf("7C128010") + "7C128010".Length;

      // Trouver l'index de fin de la sous-chaîne
      int endIndex = paceAuthenticationREQUEST.IndexOf("9000", startIndex);

      // Extraire la sous-chaîne : representant Key: z - encrypted nonce (z [PACE])
      string encryptedNonce = paceAuthenticationREQUEST.Substring(startIndex, endIndex - startIndex);

      //  the host decrypts the 128-bit nonce with PACE_nonce_AES128key derived from user input as described above
      byte[] bytesEncryptedNonce = StringToByteArray(encryptedNonce);

      // Key: K - derived key from shared secret : trouver la clé dérivée du PIN
      string derivedKeySH = calculerSHA1("3132333400000003");
      byte[] bytesDerivedKeySH = StringToByteArray(derivedKeySH);

      //  • [decrypt nonce nonce]
      byte[] bytesDecryptedNonce = decryptAES(bytesEncryptedNonce, bytesDerivedKeySH);
      string decryptedNonce = byteToHexStr(bytesDecryptedNonce);
      Org.BouncyCastle.Math.BigInteger nonce = new Org.BouncyCastle.Math.BigInteger(decryptedNonce, 16);

      //  • [generate random number as private key SKPCD for DHKA]           
      SecureRandom random = new SecureRandom();
      Org.BouncyCastle.Math.BigInteger SKPCD = new Org.BouncyCastle.Math.BigInteger(domainParams.Curve.Order.BitLength, random).Mod(domainParams.Curve.Order);

      // •  [calculate ephermeral PKPCD = BasePoint G * SKPCD]
      Org.BouncyCastle.Math.EC.ECPoint PKPCD = domainParams.G.Multiply(SKPCD).Normalize();
      // Convertir les coordonnées X et Y en hexadécimal
      string PKPCDXHex = PKPCD.XCoord.ToBigInteger().ToString(16);
      string PKPCDYHex = PKPCD.YCoord.ToBigInteger().ToString(16);

      //  • Send PKPCD and Receive PKPICC from card
      string requestPKicc = "10860000457C43814104" + PKPCDXHex + PKPCDYHex + "00";
      string reponsePKicc = executeCommande(requestPKicc);

      // Définir les positions de début et de fin de la sous-chaîne
      int entete = 10; // Après les 4 premiers octets
      int sw = reponsePKicc.Length - 4; // Avant les 4 derniers octets

      // Extraire la sous-chaîne
      reponsePKicc = reponsePKicc.Substring(entete, sw - entete);

      string PKICCXHex = reponsePKicc.Substring(0, reponsePKicc.Length / 2);
      string PKICCYHex = reponsePKicc.Substring(reponsePKicc.Length / 2);

      Org.BouncyCastle.Math.EC.ECPoint PKPICC = curve.Curve.CreatePoint(new Org.BouncyCastle.Math.BigInteger(PKICCXHex, 16), new Org.BouncyCastle.Math.BigInteger(PKICCYHex, 16));

      //  • [build shared secret: H = PKPICC * SKPCD = G * SKPICC * SKPCD]
      Org.BouncyCastle.Math.EC.ECPoint H = PKPICC.Multiply(SKPCD).Normalize();

      //  • [build new base point:] :  Gmap = G * s + H = G * s + G * SKPICC * SKPCD = G * (s + SKPICC * SKPCD)
      Org.BouncyCastle.Math.EC.ECPoint Gmap = basePoint.Multiply(nonce); Gmap = Gmap.Add(H).Normalize();

      //  • [generate random number as private key SKPCD,map for DHKA]            
      Org.BouncyCastle.Math.BigInteger SKPCDMap = new Org.BouncyCastle.Math.BigInteger(Gmap.Curve.Order.BitLength, random).Mod(Gmap.Curve.Order);

      //  • [calculate ephermeral PKPCD,map = BasePoint Gmap * SKPCD,map]

      Org.BouncyCastle.Math.EC.ECPoint PKPCDMap = Gmap.Multiply(SKPCDMap).Normalize();
      // Convertir les coordonnées X et Y en hexadécimal
      string PKPCDMapXHex = PKPCDMap.XCoord.ToBigInteger().ToString(16);
      string PKPCDMapYHex = PKPCDMap.YCoord.ToBigInteger().ToString(16);

      //  • Send PKPCD,map and Receive PKPICC,map from card
      string requestPKpcdmap = "10860000457c43834104" + PKPCDMapXHex + PKPCDMapYHex + "00";
      string responsePKpcdmap = executeCommande(requestPKpcdmap);
      sw = responsePKpcdmap.Length - 4;
      // Extraire la sous-chaîne
      if (responsePKpcdmap.Length > 4)
      {
          responsePKpcdmap = responsePKpcdmap.Substring(entete, sw - entete);
      }          

      string PKICCMapXHex = responsePKpcdmap.Substring(0, responsePKpcdmap.Length / 2);
      string PKICCMapYHex = responsePKpcdmap.Substring(responsePKpcdmap.Length / 2);

      Org.BouncyCastle.Math.EC.ECPoint PKPICCMap = curve.Curve.CreatePoint(new Org.BouncyCastle.Math.BigInteger(PKICCMapXHex, 16), new Org.BouncyCastle.Math.BigInteger(PKICCMapYHex, 16));

      // •  [build Hmap = PKPICC,map * SKPCD,map = Gmap * SKPICC,map * SKPCD,map]
      Org.BouncyCastle.Math.EC.ECPoint Hmap = PKPICCMap.Multiply(SKPCDMap).Normalize();
      byte[] HmapBytes = StringToByteArray(Hmap.XCoord.ToBigInteger().ToString(16));

      // •  Derive session keys Kenc and Kmac  from Hmap
      string Kenc = calculerSHA1(Hmap.XCoord.ToBigInteger().ToString(16) + "00000001");
      string Kmac = calculerSHA1(Hmap.XCoord.ToBigInteger().ToString(16) + "00000002");

      // •  Calculate token: TPICC = MAC(Kmac , PKPCD,map), TPCD = MAC(Kmac, PKPICC,map)
      var prefix_hex = "7f494f060a04007f000702020402028641";
      var pk_picc_map = "04" + PKICCMapXHex + PKICCMapYHex; // change

      var k_mac = StringToByteArray(Kmac);
      var cmac_t_pcd = StringToByteArray(prefix_hex + pk_picc_map);

      var macBlock = new CMac(new AesEngine());
      macBlock.Init(new KeyParameter(k_mac));
      macBlock.BlockUpdate(cmac_t_pcd, 0, cmac_t_pcd.Length);
      var t_pcd = new byte[16];
      macBlock.DoFinal(t_pcd, 0);
      addLogMsg("Kenc: " + pk_picc_map);

      var segment = new ArraySegment<byte>(t_pcd, 0, 8);
      string auth_token_host = BitConverter.ToString(segment.ToArray()).Replace("-", "").ToUpper();
      addLogMsg(" token host: " + auth_token_host);

      string tpicc = executeCommande("008600000c7c0a8508" + auth_token_host);
      sw = tpicc.Length - 4;
      string auth_token_tpicc = tpicc.Substring(8, sw - 8);
      addLogMsg(" token picc : " + auth_token_tpicc);

      // trying to read DG4 after PACE auth : 
      string readData1 = executeCommande("00a4040c07a0000002471001");           
      string selectFile2 = executeCommande("00a4020c020104");
      string readData3 = executeCommande("00b0000005");
  }

这是我的函数日志:

Send=>00a4020c020104
Get<=6982

Send=>00b0000005
Get<=6986

Send=>00A40000023F00
Get<=9000

Send=>00a4020c02011c
Get<=9000

Send=>00b0000005
Get<=315E3012069000

Send=>00b000055b
Get<=0A04007F0007020204020202010202010C3023060904007F000702020C033003020108301103030435E08301018401048501008601103023060904007F000702020C043003020109301103030431008301018401048501008601109000

Send=>0022c1a412800a04007f0007020204020283010384010c
Get<=9000


Send=>10860000027c0000
Get<=7C1280101DCB30E6EAAAA25AA65051424A1798469000


Send=>10860000457C438141047a47647acf6dc99d19a4964edd003172a3743feff697a20aaec6812d6f71deb32151b28d0a4b727477c73b948ccf31d82814e48c3138030436cdb608246489a500
Get<=7C43824104A7D1531EDF9A74D66F59FCFC6DDD2B1F7762AC725EFCC765353A0A243239BFE139E49C88C78F8E8B02E6BCF00F55B36FD2794C3E43FCB9E5642D30D88C2308DC9000


Send=>10860000457c43834104473cd341ca28fa8d7b1ddd3b102ddb8f50b68e94c8481636560edf6df8accd5ef8a4b12457e2e13baca887a3a4a16157425d00d4706b9c545817f929262e6c3200
Get<=7C43844104D23396849FB9C28880C05B566A5018EB67AB085E6034738682D311DF5865DA8159CAB7EC25A1FA8084DCC6C9AFAF356367DE42C8C543B377ABE39F2A5C00F9BF9000

Kenc: 04D23396849FB9C28880C05B566A5018EB67AB085E6034738682D311DF5865DA8159CAB7EC25A1FA8084DCC6C9AFAF356367DE42C8C543B377ABE39F2A5C00F9BF
token host: 3CEB1726FBBD656B

Send=>008600000c7c0a85083CEB1726FBBD656B
Get<=7C0A860810ECEA6E039E39409000

token picc : 10ECEA6E039E3940

Send=>00a4040c07a0000002471001
Get<=9000

Send=>00a4020c020104
Get<=6982

Send=>00b0000005
Get<=6986

6982 表示:SW_SECURITY_STATUS_NOT_SATISFIED 不满足访问条件 由于我无法选择 DG4,当我从我所在的文件(DF)执行读取命令时,收到错误6986SW_COMMAND_NOT_ALLOWED是正常的。

有谁知道在终端和芯片之间交换令牌后我需要做什么才能读取PACE在读取模式下保护的数据组?

见 :https://www.icao.int/publications/Documents/9303_p11_cons_en.pdf(参见第 91 页:D.4 安全消息传递

c# apdu elliptic-curve ecdh
1个回答
0
投票

首先,祝贺实施 PACE,这对大多数开发人员来说并不是一件容易的事。 DG4(指纹)通常不存在。如果存在,通常会使用 EAC 进行保护。

PACE 仅在确保读者有权访问文档或至少访问 CAN 或 MRZ 的意义上建立读者身份验证。访问受保护的生物识别数据需要读取器拥有由颁发国家/地区控制的 CVCA 证书/私钥信任/签名的 DS 证书。然后,该 DS 证书和私钥可用于创建 IS 证书和私钥组合,以访问 DG3 和 DG4。

© www.soinside.com 2019 - 2024. All rights reserved.