代码之家  ›  专栏  ›  技术社区  ›  Jimenemex

如何使用私钥进行身份验证-沃尔玛联盟

  •  0
  • Jimenemex  · 技术社区  · 3 年前

    我正在尝试使用Walmart附属API,它使用公共/私有令牌进行身份验证。我很难弄清楚我在台阶上遗漏了什么 provided

    我现在有一个 DelegatingHandler 添加所需的标题值。我正在使用BouncyCastle来帮助进行私有令牌签名,这就是我目前所拥有的。

        public static string Generate(string version, string consumerId, string timestamp)
        {
            // Canonicalize the headers, following after the java code in the docs.
            string[] canonicalStrings = Canonicalize(version, consumerId, timestamp);
    
            // Read the file with the password protected private key
            StreamReader stream= new StreamReader(@"..\key");
            PasswordFinder finder = new PasswordFinder("1234");
    
            // Actually get the private key
            PemReader pemReader= new PemReader(stream, finder);
            AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
            RSAParameters rsa = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);
            
            // Create the RSA Provider and import the private key
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider(2048);
            provider.ImportParameters(rsa);
    
            // Sign the canonicalized data
            byte[] signedData = provider.SignData(Encoding.UTF8.GetBytes(canonicalStrings[1]), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    
            // Convert the bytes to a base-64 string.
            return Convert.ToBase64String(signedData);
        }
    
        private static string[] Canonicalize(string version, string consumerId, string timestamp)
        {
            // Follow after the java code, which just orders the keys/values.
            StringBuilder keyBuilder = new StringBuilder();
            StringBuilder valueBuilder = new StringBuilder();
            SortedDictionary<string, string> dictionary = new SortedDictionary<string, string>() { { Constants.HEADER_COMSUMER_ID, consumerId }, { Constants.HEADER_TIMESTAMP, timestamp }, { Constants.HEADER_KEY_VERSION, version } };
    
            foreach (string key in dictionary.Keys)
            {
                keyBuilder.Append($"{key.Trim()};");
                valueBuilder.AppendLine($"{dictionary[key].Trim()}");
            }
    
            return new string[] {keyBuilder.ToString(), valueBuilder.ToString()};
        }
    

    这是通过我的 签署人:

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            string version = _walmartConfig.CurrentValue.Version; // Get Version from config
            string consumerId = _walmartConfig.CurrentValue.StageConsumerId; // Get ConsumerID from config
            string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
            string signature = Generator.Generate(version, consumerId, timestamp); // Generate signature
    
            request.Headers.Add(Constants.HEADER_KEY_VERSION, version);
            request.Headers.Add(Constants.HEADER_COMSUMER_ID, consumerId);
            request.Headers.Add(Constants.HEADER_TIMESTAMP, timestamp);
            request.Headers.Add(Constants.HEADER_SIGNATURE, signature);
            return base.SendAsync(request, cancellationToken);
        }
    

    docs :

            using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy"))
            {
                HttpResponseMessage response = await _client.SendAsync(request); // This returns HTTP 401.
    
                return response.Content.ToString();
            }
    

    here 但是我使用PuTTy菜单项导出了私钥:Conversions->导出OpenSSH密钥

    -----BEGIN RSA PRIVATE KEY-----
    Proc-Type: 4,ENCRYPTED
    DEK-Info: DES-EDE3-CBC,F014B20CAD95382A
    
    0CE3...
    -----END RSA PRIVATE KEY-----
    

    我认为我正确地遵循了指南,但是我仍然从他们的API中获得http401。有人能找出我做错了什么吗?

    0 回复  |  直到 3 年前
        1
  •  0
  •   Jimenemex    3 年前

    我最终解决了这个问题,主要是通过unix终端使用OpenSSL密钥创建,但是如果它对其他人有帮助的话,这里是最终的产品。

    用法:

    string signature = Signer.SignData(Signer.Canonicalize(version, consumerId, timestamp)[1], _keyManager.Key);
    

    _keyManager.Key 通过使用BouncyCastle读取受密码保护的私钥找到。

    StreamReader sr = File.OpenText("c:\key.pem");
    PemReader pr = new PemReader(sr, new PasswordFinder("123"));
    RsaPrivateCrtKeyParameters keyPair = pr.ReadObject() as RsaPrivateCrtKeyParameters;
    return DotNetUtilities.ToRSAParameters(keyPair);
    

    Signer.SignData 实施。

    public static string SignData(string message, RSAParameters privateKey)
    {
        byte[] signedBytes;
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
            byte[] originalBytes = Encoding.UTF8.GetBytes(message);
    
            try
            {
                rsa.ImportParameters(privateKey);
    
                signedBytes = rsa.SignData(originalBytes, CryptoConfig.MapNameToOID("SHA256"));
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
            finally
            {
                rsa.PersistKeyInCsp = false;
                }
        }
    
        return Convert.ToBase64String(signedBytes);
    }
    

    为了确认它的工作,我使用了公钥来验证。公钥的获取类似于私钥。

    public static bool Verify(string originalData, string base64SignedData, RSAParameters publicKey)
    {
        bool success = false;
        byte[] signedBytes = Convert.FromBase64String(base64SignedData);
        byte[] bytesToVerify = Encoding.UTF8.GetBytes(originalData);
    
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
            try
            {
                rsa.ImportParameters(publicKey);
                SHA256 sha256 = new SHA256Managed();
    
                byte[] hashedData = sha256.ComputeHash(signedBytes);
    
                success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA256"), signedBytes);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
            finally
            {
                rsa.PersistKeyInCsp = false;
            }
        }
    
        return success;
    }
    

    把这一切放在一起作为一个测试:

    public void SignTest()
    {
        // Arrange
        string version = "1";
        string consumerId = "8644d500-eyue-47gh-9b2b-54d5a4b9d45t";
        string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
    
        StreamReader sr = File.OpenText(@"C:\privateKey.pem");
        PemReader pr = new PemReader(sr, new PasswordFinder("123"));
        RsaPrivateCrtKeyParameters keyPair = (RsaPrivateCrtKeyParameters)pr.ReadObject();
        RSAParameters rsaPrivateParameters = DotNetUtilities.ToRSAParameters(keyPair);
    
        StreamReader sr2 = File.OpenText(@"C:\publicKey.pem");
        PemReader pr2 = new PemReader(sr2, new PasswordFinder("123"));
        var keyPair2 = pr2.ReadObject();
        RSAParameters rsaPublicParameters = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPair2);
    
        string[] canonicalForm = Signer.Canonicalize(version, consumerId, timestamp);
        
        // Act
        string signedData = Signer.SignData(canonicalForm[1], rsaPrivateParameters);
        bool validated = Signer.Verify(canonicalForm[1], signedData, rsaPublicParameters);
    
        // Assert
        Assert.True(validated);
    }