Как создать хэш пароля для RabbitMQ Management HTTP API
любимая Плагин Управления RabbitMQ есть HTTP API для управления RabbitMQ через простые HTTP-запросы.
нам нужно создавать пользователей программно,и HTTP API был выбран путь. Документация скудна, но API довольно прост и интуитивно понятен.
обеспокоенный безопасностью, мы не хотим передавать пароль пользователя в обычном тексте, и API предлагает поле для отправки хэша пароля вместо этого. Цитата оттуда:
[ GET | PUT /DELETE]/api/пользователи/имя
отдельного пользователя. Чтобы поставить пользователя, вам понадобится тело что-то вроде этого:--12-->
{"password":"secret","tags":"administrator"}
или:
{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "tags":"administrator"}
ключ тегов является обязательным. Либо
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_hash
RabbitMQ хранит его без изменений
Я принимаю предложения и на других языках программирования, а не только на C#.
4 ответов
From: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=
вы должны иметь возможность изменить свой код, чтобы следовать этому процессу
для ленивых людей (таких как я;)) существует код для вычисления пароля RabbitMq с Sha512 для .NET Core.
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;
}
}
на всякий случай, полный код от 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 от christianclinton (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
удачи!
для тех, кто ищет решение Go. Приведенный ниже код генерирует 32-байтовый случайный пароль или принимает флаг с заданным паролем и хэширует его, чтобы вы могли использовать его в поле "password_hash" файла определений в rabbit
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)
}