package util import ( "crypto/rand" "encoding/hex" "fmt" ) func GenerateRandomHex(byteLen int) (string, error) { b := make([]byte, byteLen) if _, err := rand.Read(b); err != nil { return "", fmt.Errorf("generate random bytes failed: %w", err) } return hex.EncodeToString(b), nil } // generateStrongInitialPassword 为账号生成强密码:大写+小写+数字+少量符号混合, // 并通过 util.ValidatePassword 断言,确保任何后续合规复核都不会卡住(见审计 L-R10-2)。 // 长度要求 n >= 8;实际生成 n 个字符,混合字符集字面量已刻意去掉易混淆的 I/l/O/0/1。 func GenerateStrongInitialPassword(n int) (string, error) { if n < 8 { n = 8 } const ( upper = "ABCDEFGHJKMNPQRSTUVWXYZ" lower = "abcdefghjkmnpqrstuvwxyz" digits = "23456789" symbols = "!@#$%^&*" ) alphabet := upper + lower + digits + symbols // 把每个字符类至少各取 1 个放在随机位置,保证强度检查一定通过;其余位从总字母表随机。 classes := []string{upper, lower, digits, symbols} pwd := make([]byte, n) used := 0 for _, cls := range classes { c, err := randomCharFrom(cls) if err != nil { return "", err } pwd[used] = c used++ } for i := used; i < n; i++ { c, err := randomCharFrom(alphabet) if err != nil { return "", err } pwd[i] = c } if err := shuffleBytes(pwd); err != nil { return "", err } out := string(pwd) if msg := ValidatePassword(out); msg != "" { // 理论上不可达:上面已按字符类强制填充,保留断言避免字符集未来被人修改后静默失效。 return "", fmt.Errorf("generated password failed strength check: %s", msg) } return out, nil } // randomCharFrom 用 crypto/rand 无偏地从字符集中取一个字节。循环直到命中模长内,避免简单取模偏置。 func randomCharFrom(alphabet string) (byte, error) { max := len(alphabet) bucket := 256 - (256 % max) buf := make([]byte, 1) for { if _, err := rand.Read(buf); err != nil { return 0, err } if int(buf[0]) < bucket { return alphabet[int(buf[0])%max], nil } } } // shuffleBytes Fisher-Yates 洗牌,随机索引基于 crypto/rand;用于打散 generateStrongInitialPassword // 里"前 4 个字符恰好是各字符类"的固定前缀,避免格式可预测。 func shuffleBytes(buf []byte) error { for i := len(buf) - 1; i > 0; i-- { bucket := byte(i + 1) b := make([]byte, 1) if _, err := rand.Read(b); err != nil { return err } j := int(b[0] % bucket) buf[i], buf[j] = buf[j], buf[i] } return nil }