【问题标题】:Raw HMAC-SHA1 in C# -- hmac_hash() PHP equivalentC# 中的原始 HMAC-SHA1 -- hmac_hash() PHP 等效项
【发布时间】:2012-10-03 04:04:50
【问题描述】:

我正在尝试将 Desk.com 的 Multipass SSO 集成到我的网站中,但无法生成正确的 HMAC-SHA1 签名(比如错误日志)。以下是 Desk.com 网站的说明:

  1. 使用您的多通道 API 密钥和完成的多通道令牌构建一个 SHA1 HMAC。
  2. Base64 编码生成的 HMAC。

根据日志,我的多通道令牌似乎是正确的。首先,完美运行的PHP代码:

// Build an HMAC-SHA1 signature using the multipass string and your API key
$signature = hash_hmac("sha1", $multipass, $api_key, true);
// Base64 encode the signature
$signature = base64_encode($signature);

^ 请注意,hash_hmac 的“真”值是以原始二进制格式输出信息 - 我不确定我的 C# 代码中是否是这种情况

接下来,我的 C# 代码没有正常工作:

protected string getSignature(string multipass)
{
     string api_key = "my_key_goes_here";
     HMACSHA1 hmac = new HMACSHA1(Encoding.ASCII.GetBytes(api_key));
     hmac.Initialize();
     byte[] buffer = Encoding.ASCII.GetBytes(multipass);
     string signature = BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();
     return Convert.ToBase64String(Encoding.ASCII.GetBytes(signature));
}

这是(字面上)数小时搜索和尝试多种不同方式的结果。如果我能弄清楚这一点,我将不胜感激。

如果您需要参考,请查看 Desk.com 的此页面:http://dev.desk.com/docs/portal/multipass。它包含代码示例并概述了完成代码的说明。

编辑:这是我的多通道生成代码。

protected string getMultipass(UserData user_data)
        {
            // Encode the data into a JSON object
            JavaScriptSerializer s = new JavaScriptSerializer();
            string json_data = s.Serialize(user_data);

            // Acquire the Web.config appSettings
            string site_key = "my_site_here";
            string api_key = "my_key_here";
            string iv = "OpenSSL for Ruby";

            // Using byte arrays now instead of strings
            byte[] encrypted = null;
            byte[] bIV = Encoding.ASCII.GetBytes(iv);
            byte[] data = Encoding.ASCII.GetBytes(json_data);

            // XOR the first block (16 bytes)
            // once before the full XOR
            // so it gets double XORed
            for (var i = 0; i < 16; i++)
                data[i] = (byte)(data[i] ^ bIV[i]);

            // Pad using block size of 16 bytes
            int pad = 16 - (data.Length % 16);
            Array.Resize(ref data, data.Length + pad);
            for (var i = 0; i < pad; i++)
                data[data.Length - pad + i] = (byte)pad;

            // Use the AesManaged object to do the encryption
            using (AesManaged aesAlg = new AesManaged())
            {
                aesAlg.IV = bIV;
                aesAlg.KeySize = 128;

                // Create the 16-byte salted hash
                SHA1 sha1 = SHA1.Create();
                byte[] saltedHash = sha1.ComputeHash(Encoding.UTF8.GetBytes(api_key + site_key), 0, (api_key + site_key).Length);
                Array.Resize(ref saltedHash, 16);
                aesAlg.Key = saltedHash;

                // Encrypt using the AES managed object
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        csEncrypt.Write(data, 0, data.Length);
                        csEncrypt.FlushFinalBlock();
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }

            // Return the Base64-encoded encrypted data
            return Convert.ToBase64String(encrypted, Base64FormattingOptions.None)
                .TrimEnd("=".ToCharArray()) // Remove trailing "=" characters
                .Replace("+", "-") // Change "+" to "-"
                .Replace("/", "_"); // Change "/" to "_"
        }

【问题讨论】:

    标签: c# php


    【解决方案1】:

    您可以看到以下有效的代码:

    static string create(string userDetails) { 
          string accountKey = "YOUR_ACCOUNT_KEY";
          string apiKey = "YOUR_API_KEY";        
          string initVector = "OpenSSL for Ruby"; // DO NOT CHANGE
    
          byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
          byte[] keyBytesLong;
          using( SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider() ) {
            keyBytesLong = sha.ComputeHash( Encoding.UTF8.GetBytes( apiKey + accountKey ) );
          }
          byte[] keyBytes = new byte[16];
          Array.Copy(keyBytesLong, keyBytes, 16);
    
          byte[] textBytes = Encoding.UTF8.GetBytes(userDetails);
          for (int i = 0; i < 16; i++) {
            textBytes[i] ^= initVectorBytes[i];
          }
    
          // Encrypt the string to an array of bytes
          byte[] encrypted = encryptStringToBytes_AES(textBytes, keyBytes, initVectorBytes);
          string encoded = Convert.ToBase64String(encrypted);   
          return HttpUtility.UrlEncode(encoded);
        }
    
        static byte[] encryptStringToBytes_AES(byte[] textBytes, byte[] Key, byte[] IV) {
          // Declare the stream used to encrypt to an in memory
          // array of bytes and the RijndaelManaged object
          // used to encrypt the data.
          using( MemoryStream msEncrypt = new MemoryStream() )
          using( RijndaelManaged aesAlg = new RijndaelManaged() )
          {
            // Provide the RijndaelManaged object with the specified key and IV.
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Padding = PaddingMode.PKCS7;
            aesAlg.KeySize = 128;
            aesAlg.BlockSize = 128;
            aesAlg.Key = Key;
            aesAlg.IV = IV;
            // Create an encrytor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor();
    
            // Create the streams used for encryption.
            using( CryptoStream csEncrypt = new CryptoStream( msEncrypt, encryptor, CryptoStreamMode.Write ) ) {
              csEncrypt.Write( textBytes, 0, textBytes.Length );
              csEncrypt.FlushFinalBlock();
            }
    
            byte[] encrypted = msEncrypt.ToArray(); 
            // Return the encrypted bytes from the memory stream.
            return encrypted;
          }
        }
    

    我希望它对你有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 2014-04-15
      • 1970-01-01
      相关资源
      最近更新 更多