摆在我面前的任务是将用户提供的纯文本字符串(即密码),转化为可以插入到 /etc/shadow
作为哈希密码字符串,这样用户就可以用最初提供的密码登录,生成哈希值。 这是一个非常常见的事情,我们在系统管理员的世界里不断地(重新)解决。 有无数的命令行工具可以做到这一点。
然而,在这个特定的情况下,我的限制是我需要一个纯Go的解决方案,我可以在多种情况下使用(一个Cli工具,一个api,等等)。 我的第一次尝试只是使用 bcrypt
图书馆。 乍一看,它似乎具有我所需要的属性。 它是纯粹的围棋,使用起来超级简单(奖励),而且它生成的输出看起来和我想要的差不多......但它没有工作。 使用这个库产生的输出不能(例如)粘贴到 /etc/shadow
作为用户的密码,然后用户成功能够用原密码登录。
我只是想知道,有没有人在日常工作中遇到过这种需求并解决了,谁愿意分享自己的经验和(喘息)代码? 我主要是想知道是否有针对这个近似用例的库有人会推荐? 我的google fu可能只有'rona')。
我在这里分享这个,因为我试过的第一件事没有成功,既然我公开问了,我觉得也应该公开分享这个解决方案。 这就是 a 解决办法,具体到我的情况。 我并不是宣称它是... 的 解决办法,或者说没有 更好 解决方法(请发帖!)。 针对我的具体情况,下面是我想到的......。[编辑:请注意,用户Marc在我写这篇文章的时候,在上面的评论中建议看看这个库。 谢谢你,Marc,我的fu显然感觉好多了]
如上所述,我的第一次尝试只是使用bcrypt库,但没有成功。 仔细检查后,我发现bcrypt只能以一种格式输出(即使用一种算法进行散列),这显然与Linux中的密码系统不兼容(至少我的特定发行版不兼容)。 所以,虽然输出的结果看起来一般都是这样的,但细节是。
bcrypt给我的结果是: $2$10$sdfUILYhjd.HEdhjsdfgjhfdgjh.HEWjhndcjv
在第一个字段($2
),我的Linux发行版不支持类型的 $2
显然(?)(广泛支持的有 $1, $5, $6
(即md5, sha256, 和 sha512))。 似乎没有办法让我在这个函数中指定不同的算法。bcrypt
去图书馆。 所以,我四处寻找其他的解决方法。 我得出的结论是
package main
import (
"fmt"
"math/rand"
"os"
"time"
"github.com/tredoe/osutil/user/crypt/sha512_crypt"
)
func encryptPassword(userPassword string) string {
// Generate a random string for use in the salt
const charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
s := make([]byte, 8)
for i := range s {
s[i] = charset[seededRand.Intn(len(charset))]
}
salt := []byte(fmt.Sprintf("$6$%s", s))
// use salt to hash user-supplied password
c := sha512_crypt.New()
hash, err := c.Generate([]byte(userPassword), salt)
if err != nil {
fmt.Printf("error hashing user's supplied password: %s\n", err)
os.Exit(1)
}
return string(hash)
}
这个函数返回的字符串是这样的:
$6$tZeuYPZ3$3mj70WOprJj5ytFFzC8gUFYk7eymQvaR4lDg5C0WzwBAMupRAan7BaC6EAbL9Eiyi2GZR6PQIQQa.y6kZLqh6
你可以直接粘贴到 /etc/shadow
或以哈希密码的形式提供给安装程序(kickstart
, cloud-init
等),然后继续您的工作。 在我的例子中,我正在为应用程序编写一个库函数,我可以从命令行实用程序或 api 服务中调用该函数,而 api 服务则将该哈希字符串作为参数提供给 cloud-init。
HTH其他将来可能会发现这一点的人。