Goで良い感じにencrypt, decryptしたい

Go で暗号化、複合化を良い感じにやりたい。
良い感じとはプロダクションはAWS KMSを使用しつつローカルはKMSに依存しないものを使いたい。という感じ。

自分で作っても良いかなと思ったけど既存でないかと探したらThe Go Cloud Development Kitというものを見つけた。
gocloud.dev

これは Google で提供されているパッケージ群で、今回の暗号化・複合化のパッケージ以外にもPubSubやらMySQLのクライアントとか便利そうなものが色々提供されているっぽい。

github.com

ドキュメントをみる感じ下記のようにパッケージを import してあげて後はローカルの時は base64key://AWS KMSを使いたい時は awskms://{AWS KMS ID} を渡すと良い感じにやってくれる。

import (
    "gocloud.dev/secrets"
    _ "gocloud.dev/secrets/awskms"
    _ "gocloud.dev/secrets/localsecrets"
)

試してみる

まずは暗号化・複合化してくれる package を作ります。

package crypter

import (
    "context"

    "gocloud.dev/secrets"
    _ "gocloud.dev/secrets/awskms"
    _ "gocloud.dev/secrets/localsecrets"
)

type Crypter interface {
    Encrypt(context.Context, []byte) ([]byte, error)
    Decrypt(context.Context, []byte) ([]byte, error)
}

type crypter struct {
    keeper *secrets.Keeper
}

func NewCrypter(ctx context.Context, url string) (Crypter, error) {
    keeper, err := secrets.OpenKeeper(ctx, url)
    if err != nil {
        return nil, err
    }

    return &crypter{
        keeper: keeper,
    }, nil
}

func (c *crypter) Encrypt(ctx context.Context, pt []byte) ([]byte, error) {
    cipherText, err := c.keeper.Encrypt(ctx, pt)
    if err != nil {
        return nil, err
    }

    return cipherText, nil
}

func (c *crypter) Decrypt(ctx context.Context, ct []byte) ([]byte, error) {
    plainText, err := c.keeper.Decrypt(ctx, ct)
    if err != nil {
        return nil, err
    }

    return plainText, nil
}

後はこのパッケージを使うように main パッケージを定義する

package main

import (
    "context"
    "flag"
    "log"

    "github.com/hatappi/enc-dec/crypter"
)

func main() {
    var (
        text string
        url  string
    )

    flag.StringVar(&text, "text", "", "text")
    flag.StringVar(&url, "url", "", "url")
    flag.Parse()

    ctx := context.Background()

    crypter, err := crypter.NewCrypter(ctx, url)
    if err != nil {
        log.Fatal(err)
    }

    ct, err := crypter.Encrypt(ctx, []byte(text))
    if err != nil {
        log.Fatal(err)
    }

    pt, err := crypter.Decrypt(ctx, ct)
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("plainText is %s\n", pt)
}

実行

後は go build -o enc-dec でビルドして実行する。

$ ./enc-dec --text test --url base64key://
2020/03/16 00:00:00 plainText is test

$ ./enc-dec --text test --url "awskms://xxxxxx-yyyy-xxxxx-yyyyy"
2020/03/16 00:00:00 plainText is test