equivalent salt and hash in golang

huangapple go评论75阅读模式
英文:

equivalent salt and hash in golang

问题

以下是在Go中实现给定密码的加盐和哈希的示例代码:

package main

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"golang.org/x/crypto/scrypt"
)

func main() {
	// Length of salt
	PW_SALT_BYTES := 32
	// Length of scrypt hash of passwords
	PW_HASH_BYTES := 64
	// Test password
	password := "hello"

	salt := make([]byte, PW_SALT_BYTES)
	_, err := rand.Read(salt)
	if err != nil {
		fmt.Println("Error generating salt:", err)
		return
	}

	// hash(password, salt, N=1<<14, r=8, p=1, buflen=64)
	hashedPassword, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, PW_HASH_BYTES)
	if err != nil {
		fmt.Println("Error hashing password:", err)
		return
	}

	hashedPasswordString := hex.EncodeToString(hashedPassword)
	fmt.Println(hashedPasswordString)
}

这段代码使用了Go语言的crypto/randgolang.org/x/crypto/scrypt包来实现加盐和哈希。它生成一个随机的盐值,然后使用scrypt.Key函数对密码进行哈希。最后,将哈希后的密码转换为十六进制字符串并打印出来。

希望对你有帮助!

英文:

Here's an example of salting and hashing a given password in python.

import scrypt
import os

# Length of salt
PW_SALT_BYTES = 32
# Length of scrypt hash of passwords
PW_HASH_BYTES = 64
# test password
password = &quot;hello&quot;

salt = os.urandom(PW_SALT_BYTES).encode(&#39;hex&#39;)
# hash(password, salt, N=1 &lt;&lt; 14, r=8, p=1, buflen=64)
hashed_password = scrypt.hash(str(password), salt.decode(&#39;hex&#39;), buflen=PW_HASH_BYTES).encode(&#39;hex&#39;)
print(hashed_password)

Which would give us a hashed and salted string in return:-

4d1da45b401961fccb10e094ecd70ec79510f05483ca293d300bbd0024e35866ca39fe09fbc15f83a359431021a1ed9644f7d2b871b357e37a186300877edb18

How would I implement this in golang?

答案1

得分: 24

scrypt不同,一个在Golang中用于安全哈希密码的优秀库是golang.org/x/crypto/bcrypt,如下答案所述:

https://stackoverflow.com/questions/23259586/bcrypt-password-hashing-in-golang-compatible-with-node-js

使用bcrypt而不是scrypt的几个好处:

  1. 在哈希密码时,盐会自动(随机)生成,因此您不必担心盐的生成。
  2. 在将哈希密码存储在数据库中时,您不再需要担心为每个密码哈希存储盐。
  3. 哈希和检查密码的语法更简化。
  4. bcrypt生成的哈希包括bcrypt版本、成本、盐和密码,而不仅仅是密码。

以下是从上述答案中提取的使用bcrypt的示例:

package main

import (
    "golang.org/x/crypto/bcrypt"
    "fmt"
)

func main() {
    password := []byte("MyDarkSecret")

    // 使用默认成本对密码进行哈希
    hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(hashedPassword))

    // 比较密码和哈希值
    err = bcrypt.CompareHashAndPassword(hashedPassword, password)
    fmt.Println(err) // nil表示匹配成功
}
英文:

Rather than using scrypt, a great library for securely hashing passwords with random salts in Golang is golang.org/x/crypto/bcrypt, as mentioned in the following answer:

https://stackoverflow.com/questions/23259586/bcrypt-password-hashing-in-golang-compatible-with-node-js

A couple benefits of using bcrypt instead of scrypt:

  1. The salt is automatically (and randomly) generated upon hashing a password, so that you don't have to worry about salt generation.
  2. When storing hashed passwords in a database, you no longer have to worry about storing the salt for each password hash as well.
  3. The syntax is simplified for hashing and checking passwords.
  4. The hash produced by bcrypt includes the bcrypt version, cost, salt and cipher, not only the cipher.

Here's an example of using bcrypt taken from the above answer:

package main

import (
    &quot;golang.org/x/crypto/bcrypt&quot;
    &quot;fmt&quot;
)

func main() {
    password := []byte(&quot;MyDarkSecret&quot;)

    // Hashing the password with the default cost of 10
    hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(hashedPassword))

    // Comparing the password with the hash
    err = bcrypt.CompareHashAndPassword(hashedPassword, password)
    fmt.Println(err) // nil means it is a match
}

答案2

得分: 21

Go语言标准库中没有scrypt,但在go.crypto仓库中有一个“官方”实现。

import (
	"crypto/rand"
	"fmt"
	"io"
	"log"

	"code.google.com/p/go.crypto/scrypt"
)

const (
	PW_SALT_BYTES = 32
	PW_HASH_BYTES = 64

	password = "hello"
)

func main() {
	salt := make([]byte, PW_SALT_BYTES)
	_, err := io.ReadFull(rand.Reader, salt)
	if err != nil {
		log.Fatal(err)
	}

	hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, PW_HASH_BYTES)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%x\n", hash)
}

以上是要翻译的内容。

英文:

Go doesn't have scrypt in the standard library but there is an "official" implementation in the go.crypto repo.

import (
	&quot;crypto/rand&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;log&quot;

	&quot;code.google.com/p/go.crypto/scrypt&quot;
)

const (
	PW_SALT_BYTES = 32
	PW_HASH_BYTES = 64

	password = &quot;hello&quot;
)

func main() {
	salt := make([]byte, PW_SALT_BYTES)
	_, err := io.ReadFull(rand.Reader, salt)
	if err != nil {
		log.Fatal(err)
	}

	hash, err := scrypt.Key([]byte(password), salt, 1&lt;&lt;14, 8, 1, PW_HASH_BYTES)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf(&quot;%x\n&quot;, hash)
}

答案3

得分: 6

看起来现在Go语言的官方库中有scrypt。它的subrepository x/crypto 中有一个scrypt,还有其他许多加密函数。

以下是如何使用它的示例代码:

package main

import (
	"golang.org/x/crypto/scrypt"
	"fmt"
)

func main(){
	salt := []byte("asdfasdf")
	dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)

	fmt.Println(dk)
	fmt.Println(err)
}
英文:

It looks like now Go has scrypt in official library. Its subrepository x/crypto among many other crypto functions has an scrypt.

Here is an example of how you can use it:

package main

import (
	&quot;golang.org/x/crypto/scrypt&quot;
	&quot;fmt&quot;
)

func main(){
	salt := []byte(&quot;asdfasdf&quot;)
	dk, err := scrypt.Key([]byte(&quot;some password&quot;), salt, 16384, 8, 1, 32)

	fmt.Println(dk)
	fmt.Println(err)
}

答案4

得分: 2

这是我基于RFC 2898 / PKCS #5 v2.0编写的完整哈希工具函数。

Hash函数可以用于对密码进行哈希,例如Hash("hello")

Verify函数可以用于将原始密码与哈希进行比较,基本上它的功能是对原始字符串进行哈希,并将其与实际哈希进行比较。

以下是代码部分:

package common

import (
	"crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
	"io"
	"strconv"
	"strings"
)

const (
	SALT_BYTE_SIZE    = 24
	HASH_BYTE_SIZE    = 24
	PBKDF2_ITERATIONS = 1000
)

func Hash(password string) (string, error) {
	salt := make([]byte, SALT_BYTE_SIZE)
	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
		fmt.Print("生成随机盐错误")
		return "", errors.New("生成随机盐错误")
	}

	//todo: enhance: randomize itrs as well
	hbts := pbkdf2.Key([]byte(password), salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE, sha1.New)
	//hbtstr := fmt.Sprintf("%x", hbts)

	return fmt.Sprintf("%v:%v:%v",
		PBKDF2_ITERATIONS,
		base64.StdEncoding.EncodeToString(salt),
		base64.StdEncoding.EncodeToString(hbts)), nil
}

func Verify(raw, hash string) (bool, error) {
	hparts := strings.Split(hash, ":")

	itr, err := strconv.Atoi(hparts[0])
	if err != nil {
		fmt.Printf("错误的哈希值 %v", hash)
		return false, errors.New("错误的哈希值,迭代次数无效")
	}
	salt, err := base64.StdEncoding.DecodeString(hparts[1])
	if err != nil {
		fmt.Print("错误的哈希值,盐错误:", err)
		return false, errors.New("错误的哈希值,盐错误:" + err.Error())
	}

	hsh, err := base64.StdEncoding.DecodeString(hparts[2])
	if err != nil {
		fmt.Print("错误的哈希值,哈希错误:", err)
		return false, errors.New("错误的哈希值,哈希错误:" + err.Error())
	}

	rhash := pbkdf2.Key([]byte(raw), salt, itr, len(hsh), sha1.New)
	return equal(rhash, hsh), nil
}

//字节比较
func equal(h1, h2 []byte) bool {
	diff := uint32(len(h1)) ^ uint32(len(h2))
	for i := 0; i < len(h1) && i < len(h2); i++ {
		diff |= uint32(h1[i] ^ h2[i])
	}

	return diff == 0
}

这是一个单元测试,可以帮助你了解如何调用这些函数:

package common

import (
	 
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestHash(t *testing.T) {
	hash, err := Hash("hello")
	assert.Nil(t, err)
	assert.NotEmpty(t, hash)
	 
}

func TestVerify(t *testing.T) {
	hash, err := Hash("hello")
	assert.Nil(t, err)
	assert.NotEmpty(t, hash)

	ok, err := Verify("hello", hash)
	assert.Nil(t, err)
	assert.True(t, ok)
}

希望对你有帮助!

英文:

Here's a complete hashing utilities funcs I wrote based on RFC 2898 / PKCS #5 v2.0.

Hash can be used to hash passwords, straight forward something like Hash(&quot;hello&quot;)

while Verify can be used to raw password against a hash, basically what it does is to hashes the raw string and compares it with the actual hash.

package common
import (
&quot;crypto/rand&quot;
&quot;crypto/sha1&quot;
&quot;encoding/base64&quot;
&quot;errors&quot;
&quot;fmt&quot;
&quot;golang.org/x/crypto/pbkdf2&quot;
&quot;io&quot;
&quot;strconv&quot;
&quot;strings&quot;
)
const (
SALT_BYTE_SIZE    = 24
HASH_BYTE_SIZE    = 24
PBKDF2_ITERATIONS = 1000
)
func Hash(password string) (string, error) {
salt := make([]byte, SALT_BYTE_SIZE)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
fmt.Print(&quot;Err generating random salt&quot;)
return &quot;&quot;, errors.New(&quot;Err generating random salt&quot;)
}
//todo: enhance: randomize itrs as well
hbts := pbkdf2.Key([]byte(password), salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE, sha1.New)
//hbtstr := fmt.Sprintf(&quot;%x&quot;, hbts)
return fmt.Sprintf(&quot;%v:%v:%v&quot;,
PBKDF2_ITERATIONS,
base64.StdEncoding.EncodeToString(salt),
base64.StdEncoding.EncodeToString(hbts)), nil
}
func Verify(raw, hash string) (bool, error) {
hparts := strings.Split(hash, &quot;:&quot;)
itr, err := strconv.Atoi(hparts[0])
if err != nil {
fmt.Printf(&quot;wrong hash %v&quot;, hash)
return false, errors.New(&quot;wrong hash, iteration is invalid&quot;)
}
salt, err := base64.StdEncoding.DecodeString(hparts[1])
if err != nil {
fmt.Print(&quot;wrong hash, salt error:&quot;, err)
return false, errors.New(&quot;wrong hash, salt error:&quot; + err.Error())
}
hsh, err := base64.StdEncoding.DecodeString(hparts[2])
if err != nil {
fmt.Print(&quot;wrong hash, hash error:&quot;, err)
return false, errors.New(&quot;wrong hash, hash error:&quot; + err.Error())
}
rhash := pbkdf2.Key([]byte(raw), salt, itr, len(hsh), sha1.New)
return equal(rhash, hsh), nil
}
//bytes comparisons
func equal(h1, h2 []byte) bool {
diff := uint32(len(h1)) ^ uint32(len(h2))
for i := 0; i &lt; len(h1) &amp;&amp; i &lt; len(h2); i++ {
diff |= uint32(h1[i] ^ h2[i])
}
return diff == 0
}

Here's unit test that would help you figuring out how to call such funcs

package common
import (
&quot;github.com/stretchr/testify/assert&quot;
&quot;testing&quot;
)
func TestHash(t *testing.T) {
hash, err := Hash(&quot;hello&quot;)
assert.Nil(t, err)
assert.NotEmpty(t, hash)
}
func TestVerify(t *testing.T) {
hash, err := Hash(&quot;hello&quot;)
assert.Nil(t, err)
assert.NotEmpty(t, hash)
ok, err := Verify(&quot;hello&quot;, hash)
assert.Nil(t, err)
assert.True(t, ok)
}

huangapple
  • 本文由 发表于 2014年4月13日 13:27:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/23039458.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定