C# 中的 PACE PIN 通用映射实现

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

我正在尝试使用椭圆曲线通过通用映射来实现 PACE PIN 身份验证。这是我的方法的开始,我有一个 6984 错误: • 发送 PKPCD,映射并从卡接收 PKPICC,映射。 6984错误意味着DATA_INVALID:给定的数据或卡中的数据(例如,保存的挑战)无效。 我尝试参考此代码https://github.com/PersoApp/docs/blob/main/PACE.md](https://github.com/PersoApp/docs/blob/main/PACE.md) 以及 ICAO 手册,其中解释了 PACE 通用映射 https://www.icao.int/publications/Documents/9303_p11_cons_en.pdf

我用以下曲线信息定制了卡片:

Using ECC-NIST 256 curve for the domain parameters

# Private ECC key (Generated from a RNG)
d_pace_ecdh_card_private=0029482318BE67844AE13D6C2CD672AE69525F9016496DF15AF141BB26E901EB
prime=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
a=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
order=FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
b=5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
G_X=
basepoint=6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
# Calculate Ecc public key (Q=d_pace_ecdh_card_private*basepoint)
pace_ecdh_public=046CEC918BFBE1198005718DEA22D2139C856B66326B66A8AF769DAADB4D4F439B56A161713158746F8D3117F097972B1F9CB2382E54A3AF2D61FAA30ED1FEF436

这是我的功能

public void paceGenericMapping()
  {
      // 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);
      // 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);

      //  • [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);

      //  • [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);
      // 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);

     }
c# bouncycastle elliptic-curve diffie-hellman ecdh
1个回答
0
投票

您当前问题中的实际问题是从私钥中确定公钥。

为了获得参考值,使用 PACE 日志中的私钥(根据您的旧帖子可以使用),

DH_PCD_SK
(在您的代码中
SKPCD
)以及使用您的计算得出的值代码使用 PACE 日志中的相应公钥进行验证,
DH_PCD_PK
(在您的代码中
PKPCD
):

Values from the PACE log:
DH_PCD_SK/SKPCD 0xe7d872d42157246e0e243e5d96934b798aa82b39d9d2b6c6a82435a6b233b1e5
DH_PCD_PK/PKPCD 0x044aef83454ddd59b48124dfcfad73979f671591bde3d812f103a54a5202f49f50850e5db442944a449ad24a50e71c5bc7f27d1034c732d4ea3b9c6f96207417bc00 
request         0x10860000457c438141044aef83454ddd59b48124dfcfad73979f671591bde3d812f103a54a5202f49f50850e5db442944a449ad24a50e71c5bc7f27d1034c732d4ea3b9c6f96207417bc00 

使用您的代码,公钥确实计算错误,从而产生以下错误请求:

request         0x10860000457C43814104d22ee3fd544730aebad66cea40125828561ef31fc2257000611cabc8d98b03de7fc6f52bbe7d39a7ef2bfbaf95b8a0012bf950fd5948e04844a204dda543b69b00

这是由于实现中的一个bug造成的,即与基点相乘后没有进行归一化。
大多数实现都使用特殊坐标来提高性能并执行坐标转换,因此最后需要进行归一化。
如果进行这种标准化,例如:

Org.BouncyCastle.Math.EC.ECPoint PKPCD = domainParams.G.Multiply(SKPCD).Normalize();

请求变为:

request         0x10860000457C438141044aef83454ddd59b48124dfcfad73979f671591bde3d812f103a54a5202f49f50850e5db442944a449ad24a50e71c5bc7f27d1034c732d4ea3b9c6f96207417bc00

因此符合参考值。


注意以下事项:

  • 该请求需要未压缩的密钥。这可以使用

    PKPCD.GetEncoded(false)
    轻松生成,即实现的更简单替代方案是

    string requestPKicc = "10860000457C438141" + Convert.ToHexString(PKPCD.GetEncoded(false)) + "00";
    

    顺便说一下,在生成未压缩密钥时,会在幕后执行隐式归一化。

  • 由于您的域参数对应于默认值,因此可以使用以下方式简化代码:

    ECDomainParameters domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
    

    但是,如果稍后在处理过程中更改域参数,则当然必须使用显式值(就像您所做的那样)。
    供您参考,域参数在 PACE Log ASN.1/DER 中编码,因此可以加载到 ASN.1/DER 解析器中,例如这里,这使得它们更容易阅读。

  • 发布的代码无法执行,并且没有可用的测试数据,因此故障排除很困难。对于进一步的问题,您应该提供一个专注于实际问题并使用 PACE 日志中的测试数据的 MCVE,正如我在答案中演示的那样。

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