20

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

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

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

[得到| 放 | 删除 ] /api/users/名称

个人用户。要放置一个用户,你需要一个看起来像这样的身体:

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

或者:

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

tags 键是强制性的。要么 要么password必须password_hash设置。

到目前为止,一切都很好,问题是:如何正确生成password_hash

密码哈希算法在 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”,那么它就像一个魅力。

这个答案让我导出了配置并查看了 RabbitMQ 正在生成的哈希值,我意识到了一些事情:

  • “1”的哈希示例:“y4xPTRVfzXg68sz9ALqeQzAram3CwnGo53xS752cDV5+Utzh”
  • 所有用户的哈希都有固定的长度
  • 哈希每次都会改变(即使密码相同)。我知道 PB2K 也对密码执行此操作,但不知道此加密属性的名称。
  • 如果我通过password_hashRabbitMQ 存储它而不做任何更改

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

4

11 回答 11

16

为了好玩,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"
于 2018-11-06T15:42:04.083 回答
14

来自:http ://rabbitmq.1065348.n5.nabble.com/Password-Hashing-td276.html

但是,如果您想自己实现该算法,则该算法非常简单。这是一个工作示例:

生成一个随机的 32 位盐:

加州 D5 08 9B

将其与密码的 UTF-8 表示形式(在本例中为“simon”)连接:

加州 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=

您应该能够修改您的代码以遵循此过程

于 2016-12-23T19:30:32.903 回答
11

对于懒惰的人(像我;)),有用于框架 .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;
    }
}
于 2017-06-28T09:04:26.573 回答
8

这是我前段时间偶然发现的一个小 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"))
于 2018-10-26T20:59:07.740 回答
3

这是这个脚本在 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
于 2019-05-23T06:53:07.260 回答
3

这是 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
于 2019-05-02T11:20:22.540 回答
3

以防万一,接下来应该是 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/上在线运行它。程序的输出将包含您的哈希。


克里斯蒂安克林顿的 Python 版本 ( https://gist.github.com/christianclinton/faa1aef119a0919aeb2e )

#!/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

玩得开心!

于 2018-05-31T22:43:29.927 回答
2

对于那些寻找 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)

}
于 2018-07-31T08:22:13.050 回答
0

尝试使用 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();
       });
   });
于 2021-04-07T21:39:11.437 回答
0

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

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
于 2022-02-21T18:50:34.583 回答
0

在这里,一种用 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;
    }
于 2019-04-09T09:01:18.843 回答