当前位置:首页 > 文章列表 > Golang > Go问答 > Go rsa.PSSOptions.SaltLength 该怎么选择

Go rsa.PSSOptions.SaltLength 该怎么选择

来源:17golang原创 2026-10-04 08:37:17 0浏览 收藏

如果没有外部协议约束,Go 程序内部可以使用默认的 rsa.PSSSaltLengthAuto;如果签名需要被 Java、OpenSSL、硬件设备、网关或某个明确规范验证,通常优先选择 rsa.PSSSaltLengthEqualsHash;只有对端协议明确规定固定字节数时,才填写正整数。真正要固定的不是某个“更安全”的数字,而是签名端和验证端共同遵守的契约。

Go 标准库文档:https://pkg.go.dev/crypto/rsa

先用这张表做选择

场景推荐 SaltLength主要理由
纯 Go、两端都由同一团队控制PSSSaltLengthAuto签名使用可容纳的最大盐长,验证可自动检测
跨语言、跨厂商或协议互操作PSSSaltLengthEqualsHash盐长等于摘要长度,约定清晰,常见实现容易对齐
外部规范明确写了固定字节数对应正整数严格服从协议,不自行替换成 Auto
不知道对端要求先查协议,不猜能在 Go 中签出来不代表外部一定能验证

SaltLength 控制的是 RSASSA-PSS 编码中的随机盐长度。它不是密钥长度,也不是摘要长度本身。不同盐长可以产生合法 PSS 签名,但互操作系统未必接受所有合法取值,因此工程上首先要解决协议一致性。

三个取值分别代表什么

rsa.PSSSaltLengthAuto 的值是 0。签名时它让 Go 使用密钥和哈希允许的尽可能大的盐;验证时表示自动检测签名中的盐长。把 SignPSS 的 opts 传 nil,会使用默认选项,也就是这一类自动行为。

rsa.PSSSaltLengthEqualsHash 的值是 -1,表示盐长等于哈希输出长度。使用 SHA-256 时就是 32 字节,使用 SHA-384 时就是 48 字节。这个选项不会让摘要“更长”,只是把随机盐长度绑定到摘要长度。

正整数表示精确的盐长度字节数。例如 SaltLength: 20 表示 20 字节。这个能力主要用于适配已有协议或设备,不适合随意选择;指定值若超过当前密钥和哈希能容纳的范围,签名会失败,可能返回 rsa.ErrMessageTooLong。

Go RSA PSS 的三种 SaltLength 选项与哈希长度和密钥容量关系
图1:PSS 盐长选项结构图。Auto 取可用最大值,EqualsHash 绑定摘要长度,正整数用于明确的外部协议契约;这是静态说明图,不是运行截图。

最小可用写法:互操作优先用 EqualsHash

下面使用 SHA-256,并把签名端和验证端都设为 PSSSaltLengthEqualsHash。这样盐长明确为 32 字节,配置可以直接写进接口文档和测试向量。

package pssdemo

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "fmt"
)

var pssOptions = &rsa.PSSOptions{
    // 盐长固定为摘要长度,便于和其他语言或设备对齐。
    SaltLength: rsa.PSSSaltLengthEqualsHash,
    Hash:       crypto.SHA256,
}

func Sign(priv *rsa.PrivateKey, message []byte) ([]byte, error) {
    // SignPSS 接收消息摘要,不直接接收原始消息。
    digest := sha256.Sum256(message)
    sig, err := rsa.SignPSS(rand.Reader, priv, crypto.SHA256, digest[:], pssOptions)
    if err != nil {
        return nil, fmt.Errorf("sign pss: %w", err)
    }
    return sig, nil
}

func Verify(pub *rsa.PublicKey, message, signature []byte) error {
    // 验证端使用相同哈希和相同盐长契约,拒绝其他盐长。
    digest := sha256.Sum256(message)
    if err := rsa.VerifyPSS(pub, crypto.SHA256, digest[:], signature, pssOptions); err != nil {
        return fmt.Errorf("verify pss: %w", err)
    }
    return nil
}

PSSOptions.Hash 在 SignPSS 中非零时会覆盖函数参数里的哈希;但 VerifyPSS 会忽略 opts.Hash,验证使用的是函数参数 hash。因此不要只改 options 而忘了验证调用中的 crypto.SHA256。

默认 Auto 为什么会带来跨系统风险

在纯 Go 链路中,Auto 往往很方便:签名端使用最大可用盐长,验证端自动检测,不必额外维护数字。问题出现在签名离开 Go 服务之后。某些协议、证书链、硬件模块或其他语言库会要求盐长等于哈希长度;它们可能拒绝 Go 按密钥容量生成的更长盐,即使该签名在 Go 的自动验证模式下是合法的。

这类故障常呈现为“Go 自测通过、外部验签失败”。修复方向不是反复更换密钥,而是同时核对以下四项:

  • 签名算法是否都是 RSASSA-PSS,而不是一端用了 PKCS #1 v1.5;
  • 消息摘要算法是否一致;
  • MGF1 使用的哈希是否和对端约定一致;
  • 盐长是自动、等于哈希长度,还是一个固定正整数。

如果外部系统的文档明确要求 saltLength = digestLength,Go 端就应使用 PSSSaltLengthEqualsHash,不要把 Auto 当成“自动兼容”。Auto 只描述 Go API 的选择或检测方式,不代表对端协议会接受任意盐长。

跨语言时优先固定契约

签名数据跨服务、跨队列或跨组织流动时,应把“算法、哈希、盐长、密钥标识和签名编码”一起写成协议字段。不要只写“RSA-PSS”,因为这个名字不足以唯一确定盐长策略。

package pssdemo

import "crypto/rsa"

type SignaturePolicy struct {
    Algorithm  string
    Hash       string
    SaltLength int
    KeyID      string
}

var PolicyV1 = SignaturePolicy{
    // 协议记录语义值,业务代码再映射到 Go 常量。
    Algorithm:  "RSASSA-PSS",
    Hash:       "SHA-256",
    SaltLength: 32,
    KeyID:      "signing-key-v1",
}

func VerifyOptions(policy SignaturePolicy) *rsa.PSSOptions {
    // 外部契约写 32 字节时,验证端使用精确值而不是 Auto。
    return &rsa.PSSOptions{SaltLength: policy.SaltLength}
}
Go SignPSS 与外部验证器共享摘要算法盐长契约和 RSA 公钥的静态边界
图2:PSS 互操作边界结构图。两端必须共享摘要算法和盐长契约,验证端是否自动检测应由协议明确决定;这是静态结构图,不是运行证据。

协议中最好记录实际字节数,而不是把 Go 的特殊常量值 0 或 -1 直接传给其他语言。其他实现未必使用相同枚举;写“SHA-256、saltLength 32 bytes”比写“-1”更容易理解和复现。

验证端 Auto 和严格盐长怎么取舍

验证端使用 PSSSaltLengthAuto 时,会自动检测签名里的盐长。这适合兼容一批历史签名,或服务必须接受多个已批准的旧策略。代价是验证规则更宽:只要签名结构合法并通过公钥验证,不会因为盐长和当前推荐值不同而拒绝。

验证端使用 PSSSaltLengthEqualsHash 或精确正整数时,会把盐长纳入严格协议检查。新系统、单一协议版本、合规接口或跨组织签名通常更适合这种方式。若迁移期需要同时接受旧值和新值,建议按可信的协议版本选择对应验证函数,而不是先严格验证、失败后再无条件 Auto 重试。

package pssdemo

import (
    "crypto"
    "crypto/rsa"
    "crypto/sha256"
    "errors"
)

var ErrInvalidSignature = errors.New("invalid signature")

func VerifyV1(pub *rsa.PublicKey, message, signature []byte) error {
    // V1 契约固定 SHA-256 和 32 字节盐长,失败不降级到 Auto。
    digest := sha256.Sum256(message)
    opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
    if err := rsa.VerifyPSS(pub, crypto.SHA256, digest[:], signature, opts); err != nil {
        return ErrInvalidSignature
    }
    return nil
}

对外统一返回签名无效即可,不要根据“盐长错了”“摘要错了”“密钥错了”暴露过细差异。内部日志可以记录协议版本、密钥 ID 和策略 ID,但不要记录私钥或未脱敏的敏感消息。

FIPS、密钥大小和随机源的边界

当前 Go 文档说明:使用 PSSSaltLengthAuto 签名时,普通模式会尽可能使用最大盐长;在 FIPS 140-3 模式中,盐长会被限制为哈希长度。这意味着同一份 Auto 配置在不同运行模式下可能产生不同盐长。若产物需要跨环境保持完全一致的策略,应显式选择 EqualsHash 或协议规定的正整数。

签名还受 RSA 密钥容量和哈希长度限制。更大的正整数不等于更强;当编码空间不足时,SignPSS 会失败。生产代码必须检查错误,不能在失败后退回弱算法或静默缩短盐长。

SignPSS 是随机化签名,官方文档建议大多数应用使用 crypto/rand.Reader。不要用固定字节流追求“每次签名相同”;同一消息产生不同的合法 PSS 签名是正常现象,验证结果才是判断依据。

测试要覆盖策略不匹配

最小测试矩阵应包含:EqualsHash 签名由 EqualsHash 验证成功;Auto 签名由 Auto 验证成功;若 Auto 实际生成的盐长不等于哈希长度,则严格 EqualsHash 验证应失败;消息、摘要、签名或公钥任一变化都应失败。

package pssdemo_test

import (
    "crypto/rand"
    "crypto/rsa"
    "testing"

    "example.com/pssdemo"
)

func TestEqualsHashPolicy(t *testing.T) {
    // 测试使用临时 2048 位密钥,生产密钥应由受控密钥系统管理。
    priv, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        t.Fatal(err)
    }

    message := []byte("release-manifest")
    signature, err := pssdemo.Sign(priv, message)
    if err != nil {
        t.Fatal(err)
    }

    // 同一策略必须验签成功。
    if err := pssdemo.Verify(&priv.PublicKey, message, signature); err != nil {
        t.Fatalf("verify failed: %v", err)
    }

    // 修改消息后必须验签失败,不能复用原签名。
    if err := pssdemo.Verify(&priv.PublicKey, []byte("other-manifest"), signature); err == nil {
        t.Fatal("modified message was accepted")
    }
}

跨语言项目还应保存不含私钥的测试向量:原始消息或其公开样例、哈希算法、盐长、签名、公钥和预期结果。每个消费者在升级密码库或切换 FIPS 模式后都运行同一组向量,能比线上报错更早发现策略漂移。

最终决策清单

  • 只在 Go 内部闭环且没有固定协议:可以选择 Auto。
  • 跨语言、跨厂商、合规或希望配置清晰:优先 EqualsHash。
  • 对端明确规定 N 字节:使用正整数 N,并写入协议和测试。
  • 验证端是否允许 Auto 必须由兼容策略决定,不能把失败后的自动放宽当作通用降级。
  • 哈希、MGF1、盐长、密钥和签名编码需要一起核对。
  • 切换 FIPS 运行模式时,重新确认 Auto 产生的盐长行为。

一句话总结:SaltLength 的默认值适合 Go 自己管理的链路,PSSSaltLengthEqualsHash 更适合需要稳定互操作的协议,正整数只用于明确的外部要求。签名端和验证端把同一策略写进协议、代码和测试,才是最重要的选择标准。

相关问题

SHA-256 一定要使用 32 字节盐吗?

不是密码学实现层面的唯一合法值,但在选择 EqualsHash 或外部协议要求摘要等长盐时就是 32 字节。实际项目仍以双方协议为准。

VerifyPSS 传 nil 会怎样?

会使用默认选项,盐长按 Auto 处理。若你需要严格限制为摘要长度,应显式传入 PSSSaltLengthEqualsHash。

可以为了兼容把验签失败后改用 Auto 吗?

不建议无条件这样做。更安全的做法是依据可信协议版本选择已批准的策略,并为旧策略设置明确的迁移范围和截止时间。

SaltLength 越大签名越安全吗?

不能这样简单判断。盐长受密钥和编码空间约束,还必须满足互操作协议;盲目增大可能只会造成签名失败或对端无法验证。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
systemd-journald 日志限速丢弃怎么调整systemd-journald 日志限速丢弃怎么调整
上一篇
systemd-journald 日志限速丢弃怎么调整
甲壳虫ADB助手截图保存在哪里?目录查看与隐私边界说明
下一篇
甲壳虫ADB助手截图保存在哪里?目录查看与隐私边界说明
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    324次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    382次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    376次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    340次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    167次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码