| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- 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
- }
|