英文:
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/rand和golang.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 = "hello"
salt = os.urandom(PW_SALT_BYTES).encode('hex')
# hash(password, salt, N=1 << 14, r=8, p=1, buflen=64)
hashed_password = scrypt.hash(str(password), salt.decode('hex'), buflen=PW_HASH_BYTES).encode('hex')
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,如下答案所述:
使用bcrypt而不是scrypt的几个好处:
- 在哈希密码时,盐会自动(随机)生成,因此您不必担心盐的生成。
 - 在将哈希密码存储在数据库中时,您不再需要担心为每个密码哈希存储盐。
 - 哈希和检查密码的语法更简化。
 - 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:
A couple benefits of using bcrypt instead of scrypt:
- The salt is automatically (and randomly) generated upon hashing a password, so that you don't have to worry about salt generation.
 - When storing hashed passwords in a database, you no longer have to worry about storing the salt for each password hash as well.
 - The syntax is simplified for hashing and checking passwords.
 - 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 (
    "golang.org/x/crypto/bcrypt"
    "fmt"
)
func main() {
    password := []byte("MyDarkSecret")
    // 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 (
	"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)
}
答案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 (
	"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)
}
答案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("hello")
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 (
"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("Err generating random salt")
return "", errors.New("Err generating random salt")
}
//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("wrong hash %v", hash)
return false, errors.New("wrong hash, iteration is invalid")
}
salt, err := base64.StdEncoding.DecodeString(hparts[1])
if err != nil {
fmt.Print("wrong hash, salt error:", err)
return false, errors.New("wrong hash, salt error:" + err.Error())
}
hsh, err := base64.StdEncoding.DecodeString(hparts[2])
if err != nil {
fmt.Print("wrong hash, hash error:", err)
return false, errors.New("wrong hash, hash error:" + 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 < len(h1) && i < 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 (
"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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论