首頁 >後端開發 >Golang >讓我們建立一個我們可以實際使用的密碼產生器

讓我們建立一個我們可以實際使用的密碼產生器

Barbara Streisand
Barbara Streisand原創
2024-11-14 20:13:02518瀏覽

對於我們的下一個初學者項目,我們將建立一個密碼產生器,它不僅可以產生密碼,還可以加密並保存密碼 - 以便它真正發揮作用。

我們將把程式碼分成不同的文件,這樣我們就不會得到一個大的「main.go」文件。

首先,我們初始化一個 go 專案並建立一個「profile.go」文件,其中包含加密和解密密碼的邏輯。

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/hex"
    "errors"
    "io"
)

// must be 32 characters 
var key = "askdjasjdbreonfsdfibsdhfgsdfhboo"

var ErrMalformedEncryption = errors.New("malformed encryption")

// password in small letters so it is not stored
type profile struct {
    Enc, Platform, password string
}

func (p *profile) encrypt() error {
    block, err := aes.NewCipher([]byte(key))
    if err != nil {
        return err
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return err
    }

    nonce := make([]byte, gcm.NonceSize())

    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return err
    }

    enc := gcm.Seal(nonce, nonce, []byte(p.password), nil)
    p.Enc = hex.EncodeToString(enc)

    return nil
}

func (p *profile) decrypt() error {
    block, err := aes.NewCipher([]byte(key))
    if err != nil {
        return err
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return err
    }

    nsize := gcm.NonceSize()

    if len(p.Enc) 



<p>這裡我們建立一個包含 3 個欄位的設定檔結構 - Enc、平台和密碼。 Enc 將保存加密的密碼,我們為其產生密碼的服務將儲存在平台中,而密碼將保存實際產生的密碼。設定檔結構有兩種方法「加密」和「解密」。我們使用 AES - 一種對稱金鑰加密演算法來加密和解密我們的密碼。 </p>

<p>接下來我們建立一個「store.go」文件,其中包含儲存和檢索密碼的邏輯。 <br>
</p>

<pre class="brush:php;toolbar:false">package main

import (
    "encoding/gob"
    "errors"
    "os"
    "sync"
)

const filename = "profile.bin"

var (
    ErrInvalidArgs = errors.New("invalid args")
    ErrNotFound    = errors.New("not found")
)

type store struct {
    sync.RWMutex
    data map[string]*profile
}

func newStore() (*store, error) {
    s := &store{
        data: make(map[string]*profile),
    }

    if err := s.load(); err != nil {
        return nil, err
    }

    return s, nil
}

func (s *store) load() error {
    flags := os.O_CREATE | os.O_RDONLY
    f, err := os.OpenFile(filename, flags, 0644)
    if err != nil {
        return err
    }
    defer f.Close()

    info, err := f.Stat()
    if err != nil {
        return err
    }

    if info.Size() == 0 {
        return nil
    }

    return gob.NewDecoder(f).Decode(&s.data)
}

func (s *store) save() error {
    f, err := os.OpenFile(filename, os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer f.Close()

    return gob.NewEncoder(f).Encode(s.data)
}

func (s *store) find(platform string) (string, error) {
    s.RLock()
    defer s.RUnlock()

    p, ok := s.data[platform]
    if !ok {
        return "", ErrNotFound
    }

    if err := p.decrypt(); err != nil {
        return "", err
    }

    return p.password, nil
}

func (s *store) add(platform, password string) error {
    if platform == "" {
        return ErrInvalidArgs
    }

    p := &profile{
        Platform: platform,
        password: password,
    }

    if err := p.encrypt(); err != nil {
        return err
    }

    s.Lock()
    defer s.Unlock()

    s.data[platform] = p

    return s.save()
}

我們選擇 gob 檔案進行存儲,因為它們不完全是人類可讀的。如果文件洩露,您的密碼是安全的,因為它們將被加密並且難以讀取。 store 結構包含載入、尋找和儲存到 gob 檔案的方法。我們將密碼保存在字典中。我們也使用互斥體來確保字典並發安全。需要注意的重要一點是,我們不會儲存純生成的密碼 - 我們將儲存其加密值。

現在讓我們來寫幾個實際產生密碼的函數。建立一個「password.go」檔案並輸入以下內容

package main

import (
    "math"
    "math/rand"
    "slices"
    "strings"
)

const (
    half      = .5
    onethird  = .3
    onefourth = .25
)

var (
    randlowers  = randFromSeed(lowers())
    randuppers  = randFromSeed(uppers())
    randdigits  = randFromSeed(digits())
    randsymbols = randFromSeed(symbols())
)

var basicPassword = randlowers

func mediumPassword(n int) string {
    frac := math.Round(float64(n) * half)
    pwd := basicPassword(n)
    return pwd[:n-int(frac)] + randuppers(int(frac))
}

func hardPassword(n int) string {
    pwd := mediumPassword(n)
    frac := math.Round(float64(n) * onethird)
    return pwd[:n-int(frac)] + randdigits(int(frac))
}

func xhardPassword(n int) string {
    pwd := hardPassword(n)
    frac := math.Round(float64(n) * onefourth)
    return pwd[:n-int(frac)] + randsymbols(int(frac))
}

func randFromSeed(seed string) func(int) string {
    return func(n int) string {
        var b strings.Builder
        for range n {
            b.WriteByte(seed[rand.Intn(len(seed))])
        }
        return b.String()
    }
}

func lowers() string {
    var b strings.Builder
    for i := 'a'; i 



<p>在這裡,我們編寫了產生不同難度等級密碼的函數。 basicPassword 函數產生隨機小字母字串。 mediumPassword 函數從 basicPassword 函數中獲取一小部分字符,並在其中添加隨機大寫字母。 HardPassword 函數對mediumPassword 執行相同的操作,但增加了數字。 xhardPassword 執行相同操作並新增符號。 shuffle 函數完全符合您對切片的期望,而 shuffleStr 則對字串進行洗牌。 </p>

<p>現在讓我們把所有東西放在一起。建立一個「main.go」檔案並輸入以下內容<br>
</p>

<pre class="brush:php;toolbar:false">package main

import (
    "errors"
    "flag"
    "fmt"
    "log"
    "regexp"
    "strconv"
    "strings"
)

var usage = `
Usage 
-----
--get platform=[string] - Gets saved password for a platform
--set platform=[string] len=[int] level=(basic|medium|hard|xhard) - Creates and saves a password
`

var ErrUsage = errors.New(usage)

var pattern = regexp.MustCompile(`\S+=\S+`)

type level int

const (
    _ level = iota
    level_basic
    level_medium
    level_hard
    level_xhard
)

var level_key = map[string]level{
    "basic":  level_basic,
    "medium": level_medium,
    "hard":   level_hard,
    "xhard":  level_xhard,
}

type commands struct {
    get, set bool
}

func createCommands() (c commands) {
    flag.BoolVar(&c.get, "get", false, "get password for platform")
    flag.BoolVar(&c.set, "set", false, "set password for platform")
    flag.Parse()
    return
}

func (c commands) exec(store *store) (string, error) {
    switch {
    case c.get:
        return c.getPassword(store)
    case c.set:
        return c.setPassword(store)
    default:
        return "", ErrUsage
    }
}

func (c commands) getPassword(store *store) (string, error) {
    params, err := c.parse()
    if err != nil {
        return "", err
    }

    return store.find(params["platform"])
}

func (c commands) setPassword(store *store) (string, error) {
    params, err := c.parse()
    if err != nil {
        return "", err
    }

    var password string

    n, err := strconv.Atoi(params["len"])
    if err != nil {
        return "", err
    }

    if n 



<p>我們使用標誌來指定我們期望應用程式的行為方式。 「--get」取得密碼,「--set」產生並儲存密碼。若要設定密碼,使用者提供帶有標誌的參數,以指示應用程式產生和儲存的密碼類型。要取得密碼,使用者還提供參數來指定要檢索的密碼。 </p>

<p>您現在可以執行「go build」來建立二進位檔案並測試應用程式。 </p>

<p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/173158638678335.jpg?x-oss-process=image/resize,p_40" class="lazy" alt="Let"></p>


          

            
        

以上是讓我們建立一個我們可以實際使用的密碼產生器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn