【问题标题】:How to generate password_hash for RabbitMQ Management HTTP API如何为 RabbitMQ 管理 HTTP API 生成 password_hash
【发布时间】:2016-12-23 18:37:36
【问题描述】:

心爱的RabbitMQ Management Plugin 有一个HTTP API 可以通过普通的HTTP 请求来管理RabbitMQ。

我们需要以编程方式创建用户,而 HTTP API 是我们选择的方式。文档很少,但 API 非常简单直观。

考虑到安全性,我们不想以纯文本形式传递用户密码,而 API 提供了一个字段来发送密码哈希值。从那里引用:

[ 获取 |放 |删除] /api/users/名称

个人用户。要放置用户,您将需要一个外观 像这样:

{"password":"secret","tags":"administrator"}

或:

{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "tags":"administrator"}

标签键是强制性的。必须设置 passwordpassword_hash

到目前为止,一切顺利,问题是:如何正确生成password_hash

password hashing algorithm是在RabbitMQ的配置文件中配置的,我们的配置为默认的SHA256。

我正在使用 C#,以及以下代码来生成哈希:

var cr = new SHA256Managed();
var simplestPassword = "1";
var bytes = cr.ComputeHash(Encoding.UTF8.GetBytes(simplestPassword));
var sb = new StringBuilder();
foreach (var b in bytes) sb.Append(b.ToString("x2"));
var hash = sb.ToString();

这不起作用。在一些用于 SHA256 加密的在线工具中进行测试,代码正在生成预期的输出。但是,如果我们进入管理页面并手动将用户密码设置为“1”,那么它就像一个魅力。

This answer 引导我导出配置并查看 RabbitMQ 生成的哈希值,我意识到了一些事情:

  • “1”的哈希示例:“y4xPTRVfzXg68sz9ALqeQzAram3CwnGo53xS752cDV5+Utzh”
  • 所有用户的哈希都是固定长度的
  • 哈希值每次都会改变(即使密码相同)。我知道 PB2K 也对密码执行此操作,但不知道此加密属性的名称。
  • 如果我传递了password_hash,RabbitMQ 将其​​存储而不会更改

我也接受其他编程语言的建议,而不仅仅是 C#。

【问题讨论】:

  • 我相信他们散列的不是密码而是加密密码。他们可能会使用一些 RSA 算法来获取加密密码,然后为其计算哈希。
  • 为了能够生成哈希,您需要找到用于加密密码的密钥。
  • 我试图了解它们在源代码中的作用(github.com/rabbitmq/rabbitmq-management/blob/master/src/…),但我对 Erlang 和函数式语言一无所知

标签: c# rabbitmq rabbitmqadmin


【解决方案1】:

为了好玩,还有 bash 版本!

#!/bin/bash

function encode_password()
{
    SALT=$(od -A n -t x -N 4 /dev/urandom)
    PASS=$SALT$(echo -n $1 | xxd -ps | tr -d '\n' | tr -d ' ')
    PASS=$(echo -n $PASS | xxd -r -p | sha256sum | head -c 128)
    PASS=$(echo -n $SALT$PASS | xxd -r -p | base64 | tr -d '\n')
    echo $PASS
}

encode_password "some-password"

【讨论】:

  • Mac 用户:brew install coreutils
  • 喜欢这个很棒的答案
【解决方案2】:

发件人:http://rabbitmq.1065348.n5.nabble.com/Password-Hashing-td276.html

但是,如果您想实现它,该算法非常简单 你自己。这是一个有效的例子:

生成一个随机的 32 位盐:

CA D5 08 9B

将其与密码的 UTF-8 表示形式连接(在此 案例“西蒙”):

CA D5 08 9B 73 69 6D 6F 6E

取MD5哈希:

CB 37 02 72 AC 5D 08 E9 B6 99 4A 17 2B 5F 57 12

再次连接盐:

CA D5 08 9B CB 37 02 72 AC 5D 08 E9 B6 99 4A 17 2B 5F 57 12

并转成base64编码:

ytUIm8s3AnKsXQjptplKFytfVxI=

你应该能够修改你的代码来遵循这个过程

【讨论】:

  • 感谢 Derick,它就像一个魅力。给未来读者的建议:并不总是 MD5,它取决于 rabbitmq.config 上的配置(自 v3.6.0 起默认为 SHA256)
【解决方案3】:

对于懒惰的人(像我;)),有用于框架 .Net Core 的 Sha512 计算 RabbitMq 密码的代码。

public static class RabbitMqPasswordHelper
{
    public static string EncodePassword(string password)
    {
        using (RandomNumberGenerator rand = RandomNumberGenerator.Create())
        using (var sha512 = SHA512.Create())
        {
            byte[] salt = new byte[4];

            rand.GetBytes(salt);

            byte[] saltedPassword = MergeByteArray(salt, Encoding.UTF8.GetBytes(password));
            byte[] saltedPasswordHash = sha512.ComputeHash(saltedPassword);

            return Convert.ToBase64String(MergeByteArray(salt, saltedPasswordHash));
        }
    }

    private static byte[] MergeByteArray(byte[] array1, byte[] array2)
    {
        byte[] merge = new byte[array1.Length + array2.Length];
        array1.CopyTo(merge, 0);
        array2.CopyTo(merge, array1.Length);

        return merge;
    }
}

【讨论】:

  • 它也适用于 sha256。您只需将 SHA512 替换为 SHA256
【解决方案4】:

这是我前段时间偶然发现的一个小 Python 脚本(属性在脚本中),它非常适合快速生成哈希。它不做任何错误检查,所以很简单:

#!/usr/bin/env python3

# rabbitMQ password hashing algo as laid out in:
# http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2011-May/012765.html

from __future__ import print_function
import base64
import os
import hashlib
import struct
import sys

# This is the password we wish to encode
password = sys.argv[1]

# 1.Generate a random 32 bit salt:
# This will generate 32 bits of random data:
salt = os.urandom(4)

# 2.Concatenate that with the UTF-8 representation of the plaintext password
tmp0 = salt + password.encode('utf-8')

# 3. Take the SHA256 hash and get the bytes back
tmp1 = hashlib.sha256(tmp0).digest()

# 4. Concatenate the salt again:
salted_hash = salt + tmp1

# 5. convert to base64 encoding:
pass_hash = base64.b64encode(salted_hash)

print(pass_hash.decode("utf-8"))

【讨论】:

    【解决方案5】:

    以防万一,接下来应该是 Waldo 的完整代码

    //Rextester.Program.Main is the entry point for your code. Don't change it.
    //Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Security.Cryptography;
    using System.Text;
    
    namespace Rextester
    {
        public static class RabbitMqPasswordHelper
    {
        public static string EncodePassword(string password)
        {
            using (RandomNumberGenerator rand = RandomNumberGenerator.Create())
            using (var sha256 = SHA256.Create())
            {
                byte[] salt = new byte[4];
    
                rand.GetBytes(salt);
    
                byte[] saltedPassword = MergeByteArray(salt, Encoding.UTF8.GetBytes(password));
                byte[] saltedPasswordHash = sha256.ComputeHash(saltedPassword);
    
                return Convert.ToBase64String(MergeByteArray(salt, saltedPasswordHash));
            }
        }
    
        private static byte[] MergeByteArray(byte[] array1, byte[] array2)
        {
            byte[] merge = new byte[array1.Length + array2.Length];
            array1.CopyTo(merge, 0);
            array2.CopyTo(merge, array1.Length);
    
            return merge;
        }
    }
    
        public class Program
        {
            public static void Main(string[] args)
            {
                //Your code goes here
                Console.WriteLine(Rextester.RabbitMqPasswordHelper.EncodePassword("MyPassword"));
            }
        }
    }
    

    您可以在http://rextester.com/ 上在线运行它。程序的输出将包含您的哈希值。


    由 christianclinton (https://gist.github.com/christianclinton/faa1aef119a0919aeb2e) 编写的 Python 版本

    #!/bin/env/python
    import hashlib
    import binascii
    
    # Utility methods for generating and comparing RabbitMQ user password hashes.
    #
    # Rabbit Password Hash Algorithm:
    # 
    # Generate a random 32 bit salt: 
    # CA D5 08 9B 
    
    # Concatenate that with the UTF-8 representation of the password (in this 
    # case "simon"): 
    # CA D5 08 9B 73 69 6D 6F 6E 
    
    # Take the MD5 hash: 
    # CB 37 02 72 AC 5D 08 E9 B6 99 4A 17 2B 5F 57 12 
    
    # Concatenate the salt again: 
    # CA D5 08 9B CB 37 02 72 AC 5D 08 E9 B6 99 4A 17 2B 5F 57 12 
    
    # And convert to base64 encoding: 
    # ytUIm8s3AnKsXQjptplKFytfVxI= 
    # 
    # Sources:
    # http://rabbitmq.1065348.n5.nabble.com/Password-Hashing-td276.html
    # http://hg.rabbitmq.com/rabbitmq-server/file/df7aa5d114ae/src/rabbit_auth_backend_internal.erl#l204 
    
    # Test Case:
    #   print encode_rabbit_password_hash('CAD5089B', "simon")
    #   print decode_rabbit_password_hash('ytUIm8s3AnKsXQjptplKFytfVxI=')
    #   print check_rabbit_password('simon','ytUIm8s3AnKsXQjptplKFytfVxI=')
    
    def encode_rabbit_password_hash(salt, password):
        salt_and_password = salt + password.encode('utf-8').encode('hex')
        salt_and_password = bytearray.fromhex(salt_and_password)
        salted_md5 = hashlib.md5(salt_and_password).hexdigest()
        password_hash = bytearray.fromhex(salt + salted_md5)
        password_hash = binascii.b2a_base64(password_hash).strip()
        return password_hash
    
    def decode_rabbit_password_hash(password_hash):
        password_hash = binascii.a2b_base64(password_hash)
        decoded_hash = password_hash.encode('hex')
        return (decoded_hash[0:8], decoded_hash[8:])
    
    def check_rabbit_password(test_password, password_hash):
        salt, hash_md5sum = decode_rabbit_password_hash(password_hash)
        test_password_hash = encode_rabbit_password_hash(salt, test_password)
        return test_password_hash == password_hash
    

    玩得开心!

    【讨论】:

      【解决方案6】:

      这是 PowerShell 中的一个 - 在@derick-bailey opener 中提到使用 SHA512 而不是 MD5 - 但您可以通过更改 $hash$salt 来重现中间步骤

      param (
          $password
      )
      
      $rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
      $hash = [System.Security.Cryptography.SHA512]::Create()
      
      [byte[]]$salt = New-Object byte[] 4
      $rand.GetBytes($salt)
      
      #Uncomment the next 2 to replicate derick baileys sample
      #[byte[]]$salt = 0xCA, 0xD5, 0x08, 0x9B
      #$hash = [System.Security.Cryptography.Md5]::Create()
      
      #Write-Host "Salt"
      #[System.BitConverter]::ToString($salt)
      
      [byte[]]$utf8PasswordBytes = [Text.Encoding]::UTF8.GetBytes($password)
      #Write-Host "UTF8 Bytes"
      #[System.BitConverter]::ToString($utf8PasswordBytes)
      
      [byte[]]$concatenated = $salt + $utf8PasswordBytes
      #Write-Host "Concatenated"
      #[System.BitConverter]::ToString($concatenated)
      
      [byte[]]$saltedHash = $hash.ComputeHash($concatenated)
      #Write-Host "SHA512:"
      #[System.BitConverter]::ToString($saltedHash)
      
      [byte[]]$concatenatedAgain = $salt + $saltedHash
      #Write-Host "Concatenated Again"
      #[System.BitConverter]::ToString($concatenatedAgain)
      
      $base64 = [System.Convert]::ToBase64String($concatenatedAgain)
      Write-Host "BASE64"
      $base64
      

      【讨论】:

        【解决方案7】:

        这是这个脚本的 bash 版本,可以在带有 openSSL 的 BusyBox 上运行

        #!/bin/bash
        
        function get_byte()
        {
            local BYTE=$(head -c 1 /dev/random | tr -d '\0')
        
            if [ -z "$BYTE" ]; then
                BYTE=$(get_byte)
            fi
        
            echo "$BYTE"
        }
        
        function encode_password()
        {
            BYTE1=$(get_byte)
            BYTE2=$(get_byte)
            BYTE3=$(get_byte)
            BYTE4=$(get_byte)
        
            SALT="${BYTE1}${BYTE2}${BYTE3}${BYTE4}"
            PASS="$SALT$1"
            TEMP=$(echo -n "$PASS" | openssl sha256 -binary)
            PASS="$SALT$TEMP"
            PASS=$(echo -n "$PASS" | base64)
            echo "$PASS"
        }
        
        encode_password $1
        

        【讨论】:

          【解决方案8】:

          对于那些寻找 Go 解决方案的人。下面的代码将生成一个 32 字节的随机密码或使用给定密码获取一个标志并对其进行哈希处理,以便您可以在 rabbit 中定义文件的“password_hash”字段中使用

          package main
          
          import (
              "crypto/rand"
              "crypto/sha256"
              "encoding/base64"
              "flag"
              "fmt"
              mRand "math/rand"
              "time"
          )
          
          var src = mRand.NewSource(time.Now().UnixNano())
          
          func main() {
          
              input := flag.String("password", "", "The password to be encoded. One will be generated if not supplied")
          
              flag.Parse()
          
              salt := [4]byte{}
              _, err := rand.Read(salt[:])
              if err != nil {
                  panic(err)
              }
          
              pass := *input
          
              if len(pass) == 0 {
                  pass = randomString(32)
              }
          
              saltedP := append(salt[:], []byte(pass)...)
          
              hash := sha256.New()
          
              _, err = hash.Write(saltedP)
          
              if err != nil {
                  panic(err)
              }
          
              hashPass := hash.Sum(nil)
          
              saltedP = append(salt[:], hashPass...)
          
              b64 := base64.StdEncoding.EncodeToString(saltedP)
          
              fmt.Printf("Password: %s\n", string(pass))
              fmt.Printf("Hash: %s\n", b64)
          }
          
          const (
              letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
              letterIdxBits = 6                    // 6 bits to represent a letter index
              letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
              letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
          )
          
          func randomString(size int) string {
              b := make([]byte, size)
              // A src.Int63() generates 63 random bits, enough for letterIdxMax letters!
              for i, cache, remain := size-1, src.Int63(), letterIdxMax; i >= 0; {
                  if remain == 0 {
                      cache, remain = src.Int63(), letterIdxMax
                  }
                  if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
                      b[i] = letterBytes[idx]
                      i--
                  }
                  cache >>= letterIdxBits
                  remain--
              }
          
              return string(b)
          
          }
          

          【讨论】:

          • 有什么方法可以在模板语言中做到这一点。?
          【解决方案9】:

          在这里,用 Java 实现它。

              /**
               * Generates a salted SHA-256 hash of a given password.
               */
              private String getPasswordHash(String password) {
                  var salt = getSalt();
                  try {
                      var saltedPassword = concatenateByteArray(salt, password.getBytes(StandardCharsets.UTF_8));
                      MessageDigest digest = MessageDigest.getInstance("SHA-256");
                      byte[] hash = digest.digest(saltedPassword);
          
                      return Base64.getEncoder().encodeToString(concatenateByteArray(salt,hash));
                  } catch (NoSuchAlgorithmException e) {
                      e.printStackTrace();
                  }
              }
          
              /**
               * Generates a 32 bit random salt.
               */
              private byte[] getSalt() {
                  var ba = new byte[4];
                  new SecureRandom().nextBytes(ba);
                  return ba;
              }
          
              /**
               * Concatenates two byte arrays.
               */
              private byte[] concatenateByteArray(byte[] a, byte[] b) {
                  int lenA = a.length;
                  int lenB = b.length;
                  byte[] c = Arrays.copyOf(a, lenA + lenB);
                  System.arraycopy(b, 0, c, lenA, lenB);
                  return c;
              }
          

          【讨论】:

            【解决方案10】:

            尝试使用 HareDu API。如果您使用 .NET Core 2 或更高版本,请使用 https://github.com/ahives/HareDu2/blob/master/docs/README.md。如果您使用的是 .NET 5,请使用 https://github.com/ahives/HareDu3/blob/master/docs/broker-api.md。这两个库都有一个哈希函数,您可以通过它执行以下操作:

            var result = await services.GetService<IBrokerObjectFactory>()
            .CreateUser("testuser3", "testuserpwd3", "gkgfjjhfjh".ComputePasswordHash(),
               x =>
               {
                   x.WithTags(t =>
                   {
                       t.Administrator();
                   });
               });
            

            【讨论】:

              【解决方案11】:

              这是一个较老的问题,但这里有一种在 Ruby 中执行此操作的方法,以防对其他人有帮助。我最初来到这里是为了寻找一种使用 Chef 的简单方法,但对 Ruby 并不十分熟悉,因此很有可能(甚至可能)这可以做得更好/更有效。注释掉的是 the RabbitMQ documentation 中使用的示例值,以验证它是否正常工作。

              require 'securerandom' 
              require 'digest'
              require 'base64'
              
              def generate_password_hash(plain_text)
                salt = SecureRandom.random_bytes(4).bytes.to_a
                pass = plain_text.bytes.to_a
              
                #Known sample values. Should return kI3GCqW5JLMJa4iX1lo7X4D6XbYqlLgxIs30+P6tENUV2POR
                # salt = ["908DC60A"].pack("H*").unpack("C*")
                # pass = "test12".bytes.to_a
              
                arr = salt + pass
                sha256 = Digest::SHA256.base64digest(arr.pack('C*').force_encoding('utf-8'))
              
                sha256_bytes = Base64.strict_decode64(sha256).bytes.to_a
              
                arr = salt + sha256_bytes
                password_hash = Base64.encode64(arr.pack('c*')).strip!
              
                return password_hash
              end
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-09-29
                • 1970-01-01
                • 1970-01-01
                • 2013-01-19
                • 2023-03-31
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多