我正在尝试使用椭圆曲线通过通用映射来实现 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);
}
您当前问题中的实际问题是从私钥中确定公钥。
为了获得参考值,使用 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,正如我在答案中演示的那样。