Documentation
¶
Overview ¶
Package cryptox 提供独立的 HMAC-SHA256、AES-GCM 和密码学安全随机数,只使用标准库。 HMAC 适用于共享密钥的内部接口参数签名,AES-GCM 适用于分享 token 的保密与防篡改。 实例由 NewHMACSHA256 / NewAESGCM 构造并可并发复用;零值不可用。
以下分享示例需导入 github.com/bpcoder16/pixiu/cryptox。 临时生成密钥仅用于展示;生产环境应读取安全持久化的密钥,各用途独立用钥。
key, err := cryptox.RandomBytes(32)
if err != nil {
return err
}
box, err := cryptox.NewAESGCM(key)
if err != nil {
return err
}
token, err := box.EncryptToken([]byte(`{"resource":"file-42","exp":1800000300}`), "share-download")
if err != nil {
return err
}
payload, err := box.DecryptToken(token, "share-download")
if err != nil {
return err
}
// 仅在认证成功后解析 payload,并校验 exp、资源权限和撤销状态。
_ = payload
HMAC 使用独立的至少 32 字节随机密钥,以下代码还需导入 errors:
signer, err := cryptox.NewHMACSHA256(signingKey)
if err != nil {
return err
}
signature, err := signer.Sign(message)
if err != nil {
return err
}
if !signer.Verify(message, signature) {
return errors.New("invalid request signature")
}
signingKey 和 message 由应用提供;message 必须包含所有影响业务操作的字段。 signature 是完整 32 字节认证码,不可截短。 message 应按固定编码绑定用途、方法、目标 host、路径、查询、body 摘要、时间戳及 nonce。 包的外部示例展示长度前缀编码和拒绝重复 query key,应用还须验证时间窗口并原子去重。 HMAC 验签方也能签名,不能把共享密钥交给不可信客户端;签名不隐藏参数。
AES 字节接口输出 nonce(12) || ciphertext || tag(16),支持空明文与可选 AAD。 token 接口输出 v1. 加规范 Base64URL,purpose 必填,编码长度最多 MaxTokenLen 字节; 格式版本和 purpose 被认证,同一密钥跨所有实例最多加密 2^32 次,须提前轮换。 加密失败返回空结果,解密失败返回 nil 明文;本包不记录密钥、明文或 token。
RandomToken(32) 可生成不携带明文的分享凭证;有效期、撤销和一次性消费由服务端记录实现。 本包不替代 HTTPS、业务授权、重放防护或密码存储方案,也不提供 JWT/JWE。
Index ¶
Examples ¶
Constants ¶
const MaxRandomSize = 1 << 20
MaxRandomSize 是单次 RandomBytes 的最大字节数量,限制无意的大内存分配。
const MaxTokenLen = 8192
MaxTokenLen 是 token 便捷接口允许的最大编码字节长度,不包含 URL 参数名。
Variables ¶
var ( // ErrUninitialized 表示实例未经构造函数初始化。 ErrUninitialized = errors.New("cryptox: uninitialized instance") // ErrDecrypt 统一表示密文格式或认证失败,不包含不可信输入。 ErrDecrypt = errors.New("cryptox: decryption failed") // ErrTooLarge 表示输入超过对应接口的长度限制。 ErrTooLarge = errors.New("cryptox: input too large") // ErrInvalidPurpose 表示 token 用途不满足 1–256 字节限制。 ErrInvalidPurpose = errors.New("cryptox: purpose must contain 1 to 256 bytes") // ErrInvalidSize 表示请求的随机字节数量不在允许范围内。 ErrInvalidSize = errors.New("cryptox: invalid random size") )
Functions ¶
func RandomBytes ¶
RandomBytes 返回 1–MaxRandomSize 个密码学安全随机字节。 AES-256/HMAC 密钥通常选择 32 字节;实际密钥应安全持久化并按用途隔离。
func RandomToken ¶
RandomToken 将 16–6144 个随机字节编码为无填充 Base64URL;常规推荐 size=32。 它不携带可解密内容;撤销、有效期和一次性消费需配合服务端存储。
Example ¶
package main
import (
"fmt"
"github.com/bpcoder16/pixiu/cryptox"
)
func main() {
token, err := cryptox.RandomToken(32)
if err != nil {
panic(err)
}
// 服务端保存 token 的摘要及资源、有效期;一次性使用须原子消费该记录。
fmt.Println(len(token))
}
Output: 43
Types ¶
type AESGCM ¶
type AESGCM struct {
// contains filtered or unexported fields
}
AESGCM 使用标准库 AES-GCM 和随机 nonce,可并发复用;零值不可用。
func (*AESGCM) DecryptToken ¶
DecryptToken 只接受规范编码和固定 v1 格式;失败不返回明文。 只有返回 nil 错误后才能解析 payload 并检查有效期、资源和权限。
func (*AESGCM) Encrypt ¶
Encrypt 返回 nonce(12) || ciphertext || tag(16),不修改输入。 aad 被认证但不被加密或保存;解密时须提供相同字节。允许空 plaintext 和 aad。
func (*AESGCM) EncryptToken ¶
EncryptToken 生成 v1. 前缀的无填充 Base64URL 密文;purpose 为 1–256 字节的用途。 版本与 purpose 参与认证。结果最多 MaxTokenLen 字节;payload 有效期由应用管理。
Example ¶
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/bpcoder16/pixiu/cryptox"
)
func main() {
key, err := cryptox.RandomBytes(32)
if err != nil {
panic(err)
}
box, err := cryptox.NewAESGCM(key)
if err != nil {
panic(err)
}
type share struct {
Resource string `json:"resource"`
Scope string `json:"scope"`
Expires int64 `json:"exp"`
}
now := time.Now()
payload, err := json.Marshal(share{
Resource: "file-42",
Scope: "download",
Expires: now.Add(5 * time.Minute).Unix(),
})
if err != nil {
panic(err)
}
token, err := box.EncryptToken(payload, "share-download")
if err != nil {
panic(err)
}
decrypted, err := box.DecryptToken(token, "share-download")
if err != nil {
panic(err)
}
var claims share
if err := json.Unmarshal(decrypted, &claims); err != nil {
panic(err)
}
if time.Now().Unix() >= claims.Expires || claims.Resource == "" || claims.Scope != "download" {
panic("invalid share claims")
}
// 应用继续检查资源是否仍可分享及撤销状态,再执行下载。
fmt.Println(claims.Resource, claims.Scope)
}
Output: file-42 download
type HMACSHA256 ¶
type HMACSHA256 struct {
// contains filtered or unexported fields
}
HMACSHA256 使用固定密钥签名和验签,可并发复用;零值不可用。
Example ¶
package main
import (
"crypto/sha256"
"encoding/binary"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
"github.com/bpcoder16/pixiu/cryptox"
)
func main() {
// 生产密钥由安全配置加载;此处临时生成仅为完整示例。
key, err := cryptox.RandomBytes(32)
if err != nil {
panic(err)
}
signer, err := cryptox.NewHMACSHA256(key)
if err != nil {
panic(err)
}
nonce, err := cryptox.RandomToken(16)
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, "https://internal.example/jobs?queue=normal", nil)
if err != nil {
panic(err)
}
body := []byte(`{"job":"42"}`)
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
message, err := requestMessage(req, body, timestamp, nonce)
if err != nil {
panic(err)
}
signature, err := signer.Sign(message)
if err != nil {
panic(err)
}
// 接收端从请求重建相同 message,再验证签名;成功后还须校验时间窗口并原子登记 nonce。
fmt.Println(signer.Verify(message, signature))
}
// requestMessage 是应用协议示例,不是 cryptox 的 HTTP 公共 API。
// 签名放在独立 header 中;body 为读取了大小受限的请求体后取得的原始字节。
func requestMessage(req *http.Request, body []byte, timestamp, nonce string) ([]byte, error) {
query, err := url.ParseQuery(req.URL.RawQuery)
if err != nil {
return nil, err
}
for _, values := range query {
if len(values) != 1 {
return nil, fmt.Errorf("duplicate query parameter")
}
}
digest := sha256.Sum256(body)
fields := []string{
"cryptox:request:v1",
req.Method,
req.Host,
req.URL.EscapedPath(),
query.Encode(),
string(digest[:]),
timestamp,
nonce,
}
var message []byte
for _, field := range fields {
message = binary.AppendUvarint(message, uint64(len(field)))
message = append(message, field...)
}
return message, nil
}
Output: true
func NewHMACSHA256 ¶
func NewHMACSHA256(key []byte) (*HMACSHA256, error)
NewHMACSHA256 复制至少 32 字节的密钥,密钥应由 crypto/rand 生成。 持有相同密钥的验签方也具有签名能力。
func (*HMACSHA256) Sign ¶
func (s *HMACSHA256) Sign(message []byte) ([]byte, error)
Sign 返回独立持有的完整 32 字节认证码,不改变 message。
func (*HMACSHA256) Verify ¶
func (s *HMACSHA256) Verify(message, signature []byte) bool
Verify 拒绝截短的认证码,使用恒定时间比较;未初始化的实例返回 false。