You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Go语言验证/etc/shadow文件中的SHA512哈希密码

验证/etc/shadow中的SHA512密码哈希

问题描述

我的/etc/shadow文件中存储的密码哈希为$6$IcnB6XpT8xjWC$AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/,这是通过passwd或chpasswd生成的SHA512加密密码。我尝试用Go语言提取盐值后重新哈希用户输入的密码,但无法生成一致的哈希值,代码如下:

// /etc/shadow中存储的"test"的哈希值
myPwd := "$6$IcnB6XpT8xjWC$AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/"
// 提取盐值
salt := strings.Split(myPwd, "$")[2]

encoding := base64.NewEncoding("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz").WithPadding(base64.NoPadding)
decodedSalt, err := encoding.DecodeString(salt)

// 用户输入的密码
myNewPwd := "test"

newHashedPwd, err := hashPwd512(string(myNewPwd), string(decodedSalt))

// 对比哈希值
if newHashedPwd == myPwd {
   // 密码验证通过
}

// 期望生成和/etc/shadow中一致的哈希部分
// AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/
func hashPwd512(pwd string, salt string) (string, error) {
    hash := sha512.New()
    hash.Write([]byte(salt))
    hash.Write([]byte(pwd))

    encoding := base64.NewEncoding("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz").WithPadding(base64.NoPadding)

    hashedPwd := encoding.EncodeToString(hash.Sum(nil))
    return hashedPwd, nil
}

错误原因

手动实现的哈希逻辑完全不符合Unix系统中SHA512密码哈希(SHA512-crypt)的标准算法。SHA512-crypt不是简单将盐和密码拼接后做一次SHA512哈希,而是包含多次迭代、特定的密钥混合步骤、固定的格式编码规则,必须遵循该算法才能生成一致的哈希值。

正确实现

使用Go官方的golang.org/x/crypto/crypt包中的SHA512Crypt工具来验证,该包已经实现了标准的SHA512-crypt算法:

package main

import (
    "fmt"
    "strings"

    "golang.org/x/crypto/crypt/sha512_crypt"
)

func main() {
    shadowHash := "$6$IcnB6XpT8xjWC$AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/"
    inputPwd := "test"

    // 提取哈希中的盐部分($6$后面到下一个$之前的内容)
    parts := strings.Split(shadowHash, "$")
    if len(parts) != 4 || parts[1] != "6" {
        fmt.Println("无效的SHA512哈希格式")
        return
    }
    salt := "$6$" + parts[2] + "$"

    // 使用sha512_crypt验证密码
    crypt := sha512_crypt.New()
    newHash, err := crypt.Generate([]byte(inputPwd), []byte(salt))
    if err != nil {
        fmt.Printf("生成哈希失败: %v\n", err)
        return
    }

    // 对比生成的哈希和原哈希是否一致(使用strings.Compare避免时序攻击)
    if strings.Compare(newHash, shadowHash) == 0 {
        fmt.Println("密码验证通过")
    } else {
        fmt.Println("密码验证失败")
    }
}

说明

  • 依赖安装:先执行go get golang.org/x/crypto/crypt/sha512_crypt安装依赖包
  • 避免时序攻击:使用strings.Compare而非直接==对比哈希,防止攻击者通过耗时差异破解密码
  • 标准算法实现:sha512_crypt包严格遵循Unix SHA512-crypt的算法规范,确保生成的哈希和passwd命令生成的一致

内容的提问来源于stack exchange,提问作者Subhash Prajapati

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:54:55