当前位置: 首页 > 知识库问答 >
问题:

解密苹果商务聊天身份验证令牌

闻人景澄
2023-03-14

我正在查看此集成文档:

https://register.apple.com/resources/business-chat/BC_Sending_an_Auth_Msg.pdf

请参阅“如何解密身份验证令牌”部分

我用的是c#的弹力城堡1.8.3版

我有一个测试控制台应用:

namespace AppleBusinessChat45
{
    class Program
    {
        static void Main(string[] args)
        {
            var publicKey = "BNY+I93aHVkXnNWKVLdrMJLXpQ1BsyHYoiv6UNi4rDUsRx3sNNhW8FNy9yUwxYprAwwfj1ZkoJ61Fs+SwjIbGPtXi52arvSbPglyBN4uAxtP3VP3LCP4JtSEjdgsgsretA==";
            var privateKey = "pX/BvdXXUdpC79mW/jWi10Z6PJb5SBY2+aqkR/qYOjqgakKsqZFKnl0kz10Ve+BP";
            var token = "BDiRKNnPiPUb5oala31nkmCaXMB0iyWy3Q93p6fN7vPxEQSUlFVsInkJzPBBqmW1FUIY1KBA3BQb3W3Qv4akZ8kblqbmvupE/EJzPKbROZFBNvxpvVOHHgO2qadmHAjHSmnxUuxrpKxopWnOgyhzUx+mBUTao0pcEgqZFw0Y/qZIJPf1KusCMlz5TAhpjsw=";

            var decodedToken = Base64.Decode(token);
            var decodedEphemeralPublicKey = decodedToken.Take(97).ToArray();
            var encodedEphemeralPublicKeyCheck = Encoding.ASCII.GetString(Base64.Encode(decodedEphemeralPublicKey));

            if (encodedEphemeralPublicKeyCheck != "BDiRKNnPiPUb5oala31nkmCaXMB0iyWy3Q93p6fN7vPxEQSUlFVsInkJzPBBqmW1FUIY1KBA3BQb3W3Qv4akZ8kblqbmvupE/EJzPKbROZFBNvxpvVOHHgO2qadmHAjHSg==")
                throw new Exception("Public key check failed");

            X9ECParameters curveParams = ECNamedCurveTable.GetByName("secp384r1");
            ECPoint decodePoint = curveParams.Curve.DecodePoint(decodedEphemeralPublicKey);
            ECDomainParameters domainParams = new ECDomainParameters(curveParams.Curve, curveParams.G, curveParams.N, curveParams.H, curveParams.GetSeed());
            ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(decodePoint, domainParams);

            var x = ecPublicKeyParameters.Q.AffineXCoord.ToBigInteger();
            var y = ecPublicKeyParameters.Q.AffineYCoord.ToBigInteger();

            if (!x.Equals(new BigInteger("8706462696031173094919866327685737145866436939551712382591956952075131891462487598200779332295613073905587629438229")))
                throw new Exception("X coord check failed");

            if (!y.Equals(new BigInteger("10173258529327482491525749925661342501140613951412040971418641469645769857676705559747557238888921287857458976966474")))
                throw new Exception("Y coord check failed");

            var privateKeyBytes = Strings.ToByteArray(privateKey);
            ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(new BigInteger(privateKeyBytes), domainParams);

            IBasicAgreement agree = AgreementUtilities.GetBasicAgreement("ECDHC");
            agree.Init(ecPrivateKeyParameters);
            BigInteger sharedKey = agree.CalculateAgreement(ecPublicKeyParameters);
            var sharedKeyBytes = sharedKey.ToByteArrayUnsigned();

            // Expecting:
            // 2lvSJsBO2keUHRfvPG6C1RMUmGpuDbdgNrZ9YD7RYnvAcfgq/fjeYr1p0hWABeif

            Console.WriteLine("Done");
            Console.ReadLine();
        }
    }
}

文档说明“示例私钥表示为转换为字节的无符号标量。”并且具有以下值:

pX/BvdXXUdpC79mW/jWi10Z6PJb5SBY2 aqkR/qYOjqgakKsqZFKnl0kz10Ve BP

我可能读错了私钥?因为我尝试解析的私钥与留档中的预期值不匹配。

网上几乎没有实现细节。

就Bouncy Castle而言,如何从字符串中读取私钥,然后用于生成共享密钥?

更新

对于那些寻找解密apple chat身份验证令牌的完整示例实现的人,请参阅下面:

namespace AppleBusinessChat45
{
    class Program
    {
        static void Main(string[] args)
        {
            var privateKey = "pX/BvdXXUdpC79mW/jWi10Z6PJb5SBY2+aqkR/qYOjqgakKsqZFKnl0kz10Ve+BP";
            var token = "BDiRKNnPiPUb5oala31nkmCaXMB0iyWy3Q93p6fN7vPxEQSUlFVsInkJzPBBqmW1FUIY1KBA3BQb3W3Qv4akZ8kblqbmvupE/EJzPKbROZFBNvxpvVOHHgO2qadmHAjHSmnxUuxrpKxopWnOgyhzUx+mBUTao0pcEgqZFw0Y/qZIJPf1KusCMlz5TAhpjsw=";

            // #####
            // ##### Step 1
            // #####
            var decodedToken = Convert.FromBase64String(token);
            var decodedEphemeralPublicKey = decodedToken.Take(97).ToArray();
            var encodedEphemeralPublicKeyCheck = Convert.ToBase64String(decodedEphemeralPublicKey);

            if (encodedEphemeralPublicKeyCheck != "BDiRKNnPiPUb5oala31nkmCaXMB0iyWy3Q93p6fN7vPxEQSUlFVsInkJzPBBqmW1FUIY1KBA3BQb3W3Qv4akZ8kblqbmvupE/EJzPKbROZFBNvxpvVOHHgO2qadmHAjHSg==")
                throw new Exception("Public key check failed");

            X9ECParameters curveParams = ECNamedCurveTable.GetByName("secp384r1");
            ECPoint decodePoint = curveParams.Curve.DecodePoint(decodedEphemeralPublicKey);
            ECDomainParameters domainParams = new ECDomainParameters(curveParams.Curve, curveParams.G, curveParams.N, curveParams.H, curveParams.GetSeed());
            ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(decodePoint, domainParams);

            var x = ecPublicKeyParameters.Q.AffineXCoord.ToBigInteger();
            var y = ecPublicKeyParameters.Q.AffineYCoord.ToBigInteger();

            if (!x.Equals(new BigInteger("8706462696031173094919866327685737145866436939551712382591956952075131891462487598200779332295613073905587629438229")))
                throw new Exception("X coord check failed");

            if (!y.Equals(new BigInteger("10173258529327482491525749925661342501140613951412040971418641469645769857676705559747557238888921287857458976966474")))
                throw new Exception("Y coord check failed");

            Console.WriteLine("Step 1 complete");

            // #####
            // ##### Step 2
            // #####
            var privateKeyBytes = Convert.FromBase64String(privateKey);
            var ecPrivateKeyParameters = new ECPrivateKeyParameters("ECDHC", new BigInteger(1, privateKeyBytes), domainParams);
            var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(ecPrivateKeyParameters);
            var ecPrivateKey = (ECPrivateKeyParameters) PrivateKeyFactory.CreateKey(privateKeyInfo);

            IBasicAgreement agree = AgreementUtilities.GetBasicAgreement("ECDHC");
            agree.Init(ecPrivateKey);
            BigInteger sharedKey = agree.CalculateAgreement(ecPublicKeyParameters);
            var sharedKeyBytes = sharedKey.ToByteArrayUnsigned();
            var sharedKeyBase64 = Convert.ToBase64String(sharedKeyBytes);

            if (sharedKeyBase64 != "2lvSJsBO2keUHRfvPG6C1RMUmGpuDbdgNrZ9YD7RYnvAcfgq/fjeYr1p0hWABeif")
                throw new Exception("Shared key check failed");

            Console.WriteLine("Step 2 complete");

            // #####
            // ##### Step 3
            // #####
            var kdf2Bytes = Kdf2(sharedKeyBytes, decodedEphemeralPublicKey);
            var kdf2Base64 = Convert.ToBase64String(kdf2Bytes);

            if (kdf2Base64 != "mAzkYatDlz4SzrCyM23NhgL/+mE3eGgfUz9h1CFPhZOtXequzN3Q8w+B5GE2eU5g")
                throw new Exception("Kdf2 failed");

            Console.WriteLine("Step 3 complete");

            // #####
            // ##### Step 4
            // #####
            var decryptionKeyBytes = kdf2Bytes.Take(32).ToArray();
            var decryptionIvBytes = kdf2Bytes.Skip(32).ToArray();

            var decryptionKeyBase64 = Convert.ToBase64String(decryptionKeyBytes);
            var decryptionIvBase64 = Convert.ToBase64String(decryptionIvBytes);

            if (decryptionKeyBase64 != "mAzkYatDlz4SzrCyM23NhgL/+mE3eGgfUz9h1CFPhZM=")
                throw new Exception("Decryption key check failed");

            if (decryptionIvBase64 != "rV3qrszd0PMPgeRhNnlOYA==")
                throw new Exception("Decryption iv check failed");

            var encryptedDataBytes = decodedToken.Skip(97).Take(decodedToken.Length - 113).ToArray();
            var tagBytes = decodedToken.Skip(decodedToken.Length - 16).ToArray();

            var encryptedDataBase64 = Convert.ToBase64String(encryptedDataBytes);
            var tagBase64 = Convert.ToBase64String(tagBytes);

            if (encryptedDataBase64 != "afFS7GukrGilac6DKHNTH6YFRNqjSlwSCpkXDRj+")
                throw new Exception("Encrypted data check failed");

            if (tagBase64 != "pkgk9/Uq6wIyXPlMCGmOzA==")
                throw new Exception("Tag check failed");

            KeyParameter keyParam = ParameterUtilities.CreateKeyParameter("AES", decryptionKeyBytes);
            ParametersWithIV parameters = new ParametersWithIV(keyParam, decryptionIvBytes);
            IBufferedCipher cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding");
            cipher.Init(false, parameters);
            var resultBytes = cipher.DoFinal(encryptedDataBytes.Concat(tagBytes).ToArray());
            var resultBase64 = Convert.ToBase64String(resultBytes);
            var resultString = Strings.FromByteArray(resultBytes);

            if (resultString != "xXTi32iZwrQ6O8Sy6r1isKwF6Ff1Py")
                throw new Exception("Decryption failed");

            Console.WriteLine("Step 4 complete");
            Console.WriteLine(resultString);

            Console.WriteLine();
            Console.WriteLine("Done... press any key to finish");
            Console.ReadLine();
        }

        static byte[] Kdf2(byte[] sharedKeyBytes, byte[] ephemeralKeyBytes)
        {
            var gen = new Kdf2BytesGenerator(new Sha256Digest());
            gen.Init(new KdfParameters(sharedKeyBytes, ephemeralKeyBytes));

            byte[] encryptionKeyBytes = new byte[48];
            gen.GenerateBytes(encryptionKeyBytes, 0, encryptionKeyBytes.Length);
            return encryptionKeyBytes;
        }
    }
}

共有1个答案

隆飞宇
2023-03-14

替换为:

var privateKeyBytes = Strings.ToByteArray(privateKey);
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(new BigInteger(privateKeyBytes), domainParams);

IBasicAgreement agree = AgreementUtilities.GetBasicAgreement("ECDHC");
agree.Init(ecPrivateKeyParameters);

有了这个:

var privateKeyBytes = Convert.FromBase64String(privateKey);
var ecPrivateKeyParameters = new ECPrivateKeyParameters("ECDHC", new BigInteger(1, privateKeyBytes), domainParams);
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(ecPrivateKeyParameters);
var ecPrivateKey = (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(privateKeyInfo);

var agree = AgreementUtilities.GetBasicAgreement("ECDHC");
agree.Init(ecPrivateKey);

它应该有效。

 类似资料:
  • 我正在阅读apple business chat api文档,我在“接收大型交互式数据有效载荷”部分。最后一步是解密附件,然后发送到业务聊天Api。 下载 ---和破译说明文档-- 然后,使用密码库,使用AES/CTR/NoPadding算法对文件进行解密,并使用下载附件的附件字典中的密钥值,使用全零16字节初始化向量(IV)。 因此,以下是我对本文档的解释,因为它们使我几乎没有什么可处理的。 这

  • 我有一个LaravelAPI(实际上是LumenAPI)服务于VueJS前端。Vue应用程序允许用户登录到谷歌。然后将Google令牌发送回Lumen API,后者使用Google验证令牌,然后验证电子邮件地址是否为有效用户。然后它生成一个令牌,与用户一起存储在数据库中,并返回用户对象。 我没有使用Passport或jwt auth之类的东西。那么现在,我如何使用默认的Auth中间件来验证(现在已

  • 在我的Jitsi Meet Prodody配置文件中:~/。jitsi meet cfg/prosody/config/conf.d 我有以下配置: 有了它,我可以通过令牌进行身份验证。 但是,如果我不指定任何令牌,例如: https://jitsi.mydummyserver.com/test 然后我得到以下提示询问用户和密码: 有没有办法只允许令牌身份验证并完全摆脱那个提示? 谢谢

  • 我刚刚开始在.NET中开发我的第一个REST API。由于它将是无状态的,我将使用令牌进行身份验证: 基本思想(System.Security.Cryptography): null 检查凭据是否有效(用户名,将哈希密码与db值进行比较) 如果为真,则加密数据对象 对生成的令牌使用HMAC,并将其存储到数据库 将令牌(不带HMAC)返回给用户(cookie/字符串) 对需要身份验证的方法的请求:

  • 我尝试从PHP向Android手机应用发送推送通知 我使用谷歌云消息,但在浏览器中我看到错误: 请求缺少身份验证密钥(FCM令牌)。请参阅FCM文档的“认证”部分,网址为https://firebase.google.com/docs/cloud-messaging/server.错误401 此外,我可以从c#控制台应用程序发送消息推送并在我的手机上接收它(我认为,授权密钥是正确的): 问题:如何