Go 言語(以下 Golang)で OpenPGP に準拠した公開鍵と秘密鍵を作りたい。
つまり、OpenPGP 互換の鍵ペアを Golang で作ろうと思うも、Go 公式のドキュメントを見ると Deprecated
(廃止)になっていて、ガチョーン。
しかも、 golang.org/x/crypto/openpgp
を使っていると、CI/ID などの脆弱性スキャナーで CVE-2022-27191 の脆弱性を指摘される。
さらに、Go の公式は「後継のパッケージは色々あるけど、私たちの方から『これがオススメ』というものはないよ」ときたもんだ。どうしよう。
TL; DR (今北産業)
2022/11/12 現在のスター数、更新頻度、利用者数から見て
github.com/ProtonMail/go-crypto
パッケージだが、そこからOpenPGP
のみに特化させた GopenPGP がよさげ。
-
PGP のゴタゴタの後、規格化されたのが OpenPGP である。
OpenPGP はアプリの実装規格(RFC4880)なので、管理団体は存在しても OpenPGP というアプリは存在しない。「OpenPGP なんとか」というのは「OpenPGP(の機能に準拠した)なんとか」という意味。また、OpenPGP は、利用する暗号化アルゴリズムに RSA や Curve25519 などが選択できる。つまり、PGP の鍵も、アルゴリズムが RSA なら中身は実質 RSA の鍵と同じである。 -
有名な GPG(GnuPG, GNU Privacy Guard)は、OpenPGP を C 言語で実装したアプリである。
つまり、GPG も OpenPGP の 1 つ(の実装)である。他言語でも実装ライブラリや、実装アプリは多数存在する。また、Golang の OpenPGP 用パッケージも複数ある。 -
GopenPGP を検討してどうか。
Mozilla の暗号用エディタ SOPS(Go 製)で現在使っているのが Proton Mail の github.com/ProtonMail/go-crypto パッケージ。本家golang.org/x/crypto/openpgp
をフォークして現在でもメンテナンスしている。そして、そこから OpenPGP のみをスピンオフさせて使いやすくしたのが GopenPGP の github.com/ProtonMail/gopenpgp パッケージ。良さげだったら GitHub スターをつけよう。
この記事は、OpenPGP に準拠したアプリで RSA アルゴリズムを利用したい場合を想定しています。
- 単純に署名に使いたいだけなら Ed25519 を使った方がいい。(Git のコミットにも使える)
- Go 公式パッケージ: golang.org/x/crypto/ed25519
- 単純にデータを暗号化したいだけなら共有鍵暗号の AES-256 を使った方がいい。
- Go 公式パッケージ: golang.org/x/crypto/aes
- 単純に公開鍵・秘密鍵暗号を利用したいだけなら、アルゴリズムは RSA より ECDH など Curve25519(
x25519
関数の実装)を使って、AES-256 の共通暗号鍵を交換した方がいい。- Go 公式パッケージ: golang.org/x/crypto/curve25519
- GopenPGP パッケージ: github.com/ProtonMail/gopenpgp/v2
- (内部的には Go 公式のライブラリのラッパーだが、OpenPGP に準拠しつつ、Curve25519 アルゴリズムを利用する)
- 単純に最先端のハイブリッド暗号1を試したいだけなら、NIST による耐量子演算暗号アルゴリズムのコンテスト勝者の CRYSTALS チームによる「KYBER アルゴリズム」を試してみるといい。
- NIST の正式制定待ちなものの、OpenPGP のアルゴリズムとしても取り込まれる予定。
Kyber768
と + 楕円曲線暗号 X25519 の組み合わせはMUST
(必須)となることが決まっている。 - 公式の C 言語による実装: Kyber | pq-crystals @ GitHub
- Cloudflare による Go 言語実装: CIRCL 暗号ライブラリ @ GitHub
-
Kyber PKE(公開鍵暗号)
- 使い方: 【Golang】Kyber アルゴリズムで公開鍵暗号(PKE) @ Qiita
- Kyber KEM(共通鍵交換暗号)
- おまけ
- Dilithium Digital Signature(電子署名。Dilithium は、量子コンピューター耐性のある電子署名アルゴリズム・コンペの勝者の 1 つで、同上の CRYSTALS チームによるアルゴリズム)
-
Kyber PKE(公開鍵暗号)
- NIST の正式制定待ちなものの、OpenPGP のアルゴリズムとしても取り込まれる予定。
- 併せて読みたい
-
GopenPGP @ text.Baldanders.info
- GopenPGP に至った、経緯に詳しい
- GopenPGP について軽く調べておく @ Zenn.dev
-
GopenPGP @ text.Baldanders.info
TS; DR
go get "github.com/ProtonMail/gopenpgp/v2"
GolangでPGP鍵のペアを作成
package main
import (
"fmt"
"log"
"strings"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/gopenpgp/v2/helper"
)
func main() {
passphrase := []byte("MyVeryStrongSecretPhrase")
name := "My Name"
email := "my.name@example.com"
// Create RSA Secret Key (PEM 形式)
rsaBits := 4096
// 鍵のバリエーション:
//
// RSA, string
// rsaKey, err := helper.GenerateKey(name, email, passphrase, "rsa", rsaBits)
//
// Curve25519, string
// ecKey, err := helper.GenerateKey(name, email, passphrase, "x25519", 0)
//
// RSA, Key struct
// rsaKey, err := crypto.GenerateKey(name, email, "rsa", rsaBits)
//
// Curve25519, Key struct
// ecKey, err := crypto.GenerateKey(name, email, "x25519", 0)
//
// 以下は RSA-4096 の鍵ペア作成
rsaKey, err := helper.GenerateKey(name, email, passphrase, "rsa", rsaBits)
if err != nil {
log.Fatal(err)
}
fmt.Printf("RSA4096 Secret:\n%v\n\n", rsaKey)
// Generate RSA Public Key (PEM 形式)
keyRing, err := crypto.NewKeyFromArmoredReader(strings.NewReader(rsaKey))
if err != nil {
log.Fatal(err)
}
publicKey, err := keyRing.GetArmoredPublicKey()
if err != nil {
log.Fatal(err)
}
fmt.Printf("RSA4096 Public:\n%v\n", publicKey)
// Output:
// RSA4096 Secret:
// -----BEGIN PGP PRIVATE KEY BLOCK-----
// Version: GopenPGP 2.4.10
// Comment: https://gopenpgp.org
//
// xcaGBGNuTusBEAC16zBOWYqd6uEK7aUIHz3VKGXc9X/94yXaIq4xdVJd/MZp/379
// 8lfWxkwuN/+KT2TXMQerq7s765ZI1t25p+YPtzXg+fr4JdVlx6EvhGatuCstBTys
// ** snip **
// Bd4MBg+SCbI9+X04nDtIOnFQjv7Vw3l9PGe1HXhCOZlIZXZ3x2kKCjLBZn+/qi9b
// lMHXXA3VMuZ5IeHdL5EZJXPbhFhU/7Ijw1h2pqoUq/zQoRMLXQ==
// =bgBm
// -----END PGP PRIVATE KEY BLOCK-----
//
// RSA4096 Public:
// -----BEGIN PGP PUBLIC KEY BLOCK-----
// Version: GopenPGP 2.4.10
// Comment: https://gopenpgp.org
//
// xsFNBGNuTusBEAC16zBOWYqd6uEK7aUIHz3VKGXc9X/94yXaIq4xdVJd/MZp/379
// 8lfWxkwuN/+KT2TXMQerq7s765ZI1t25p+YPtzXg+fr4JdVlx6EvhGatuCstBTys
// ** snip **
// dKRdba+jzP/OIn8YJsEF3gwGD5IJsj35fTicO0g6cVCO/tXDeX08Z7UdeEI5mUhl
// dnfHaQoKMsFmf7+qL1uUwddcDdUy5nkh4d0vkRklc9uEWFT/siPDWHamqhSr/NCh
// Ewtd
// =mwx1
// -----END PGP PUBLIC KEY BLOCK-----
}