使用 Go 生成适用于 '/etc/shadow' 的散列密码字符串



摆在我面前的任务是获取用户提供的纯文本字符串(即密码(,并将其转换为可以作为散列密码字符串插入/etc/shadow的东西,以便用户可以使用最初提供的密码登录以生成哈希。 这是我们在系统管理世界中一遍又一遍地(重新(解决的非常普遍的事情。 有无数的命令行实用程序可以执行此操作。

但是,在这种特定情况下,我的约束是我需要一个可以在多个上下文(cli 工具、API 等(中使用的纯 Go 解决方案。 我的第一次尝试只是使用bcrypt库。 乍一看,它似乎拥有我需要的属性。 它是纯粹的 Go,使用起来超级简单(奖励(,它生成的输出看起来就像我所追求的......但它没有用。 例如,使用此库产生的输出不能作为用户密码粘贴到/etc/shadow中,然后用户能够使用原始密码成功登录。

我只是想知道是否有人在日常工作中遇到过这种需求并解决了它,谁愿意分享他们的经验和(喘息(代码? 我主要想知道是否有一个库针对有人会推荐的这个近似用例? (我的谷歌 FU 可能只有"罗娜"(。

我在这里分享这个是因为我尝试的第一件事不起作用,而且由于我公开询问,我觉得公开分享解决方案是公平的。 这是一个解决方案,特定于我的情况。 我并不是宣称它是解决方案,或者没有更好的解决方案(请发布这些解决方案! 针对我的具体情况,这是我想到的...[编辑:请注意,用户 Marc 建议在我撰写本文时在上面的评论中查看此库。 谢谢你,马克,我的福显然感觉好多了]

如前所述,我的第一次尝试只是使用 bcrypt 库,但它没有奏效。 经过仔细检查,我发现 bcrypt 只以一种格式输出(即使用一种算法进行哈希处理(,这显然与 Linux 中的密码系统不兼容(至少不是我的特定发行版(。 因此,虽然输出通常看起来是它应该的样子,但细节是:

bcrypt给了我这样的东西:$2$10$sdfUILYhjd.HEdhjsdfgjhfdgjh.HEWjhndcjv

在第一个领域($2(,我的Linux发行版显然不支持类型$2(?((广泛支持的$1, $5, $6(即md5,sha256和sha512((。 我似乎没有办法在bcryptgo 库中指定不同的算法。 因此,我投身于其他方法/解决方案。 我得出的结论是:

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: %sn", err)
os.Exit(1)
}
return string(hash)
}

此函数返回如下所示的字符串:

$6$tZeuYPZ3$3mj70WOprJj5ytFFzC8gUFYk7eymQvaR4lDg5C0WzwBAMupRAan7BaC6EAbL9Eiyi2GZR6PQIQQa.y6kZLqh6

您可以简单地将其直接粘贴到/etc/shadow中或作为散列密码(kickstartcloud-init等(提供给安装程序,然后开展业务。 就我而言,我正在为一个应用程序编写一个库函数,我可以从命令行实用程序或 api 服务调用该函数,而 api 服务又将该散列字符串作为参数提供给 cloud-init。

HTH其他人将来可能会发现这一点。

最新更新