Go 言語(以下 golang)で、RFC9580(packet v6)に準拠した OpenPGP 鍵を生成しつつ、鍵に Notation 情報も埋め込みたい。
TL; DR (とりあえずソースコード)
まずは RFC9580 準拠の OpenPGP 鍵を生成するコードです。
ポイントは、単純に鍵を生成する方法と違い、プロファイル(profile.Custom
)の SetKeyAlgorithm
フィールド内の関数変数をオーバーライドしている箇所です。
また、この例ではセキュリティ・レベルを "High
" に設定しています。そのため内部の楕円曲線方式が Ed25519
(Standard
)から Ed448
(High
)に強化されています。
go version go1.24.4 darwin/amd64
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/ProtonMail/gopenpgp/v3/constants"
"github.com/ProtonMail/gopenpgp/v3/crypto"
"github.com/ProtonMail/gopenpgp/v3/profile"
)
func main() {
yourName := "myName"
yourEmail := "my@example.com"
yourDomain := "example.com"
yourWeb := "https://blog.example.com/"
// Create a new profile based on RFC9580.
// * Ref: https://www.ietf.org/rfc/rfc9580.pdf
profRFC9580 := profile.RFC9580()
// Override the SetKeyAlgorithm function variable to set custom notations.
tmpSetKeyAlgorithm := profRFC9580.SetKeyAlgorithm
profRFC9580.SetKeyAlgorithm = func(cfg *packet.Config, securityLevel int8) {
tmpSetKeyAlgorithm(cfg, securityLevel)
// Set Notations for the key.
cfg.SignatureNotations = []*packet.Notation{
{
Name: "ariadne.id",
Value: []byte("dns:" + yourDomain),
IsHumanReadable: true,
},
{
Name: "ariadne.id",
Value: []byte(yourWeb),
IsHumanReadable: true,
},
}
}
// Create a PGP handler with the RFC9580 profile.
pgpHandler := crypto.PGPWithProfile(profRFC9580)
// Create a new PGP key generator with the desired user ID.
pgpGenerator := pgpHandler.KeyGeneration().AddUserId(yourName, yourEmail).New()
// Generate the key with high security level. (choices: constants.StandardSecurity)
privKey, err := pgpGenerator.GenerateKeyWithSecurity(constants.HighSecurity)
if err != nil {
panic(err)
}
// Get the ASCII armored private key
fmt.Println("Private Key (ASCII Armored):")
privKeyAsc, err := privKey.Armor()
if err != nil {
panic(err)
}
fmt.Println(privKeyAsc)
// Get the ASCII armored public key
fmt.Println("Public Key (ASCII Armored):")
pubKeyAsc, err := privKey.GetArmoredPublicKey()
if err != nil {
panic(err)
}
fmt.Println(pubKeyAsc)
// Save the public key to a file
pathFileKey := filepath.Join("..", "testdata", "pubKey_RFC9580_w_notations.asc")
if err := os.WriteFile(pathFileKey, []byte(pubKeyAsc), 0644); err != nil {
panic(err)
}
}
-
オンラインで動作をみる @ GoPlayground
- モジュールが重いので、たまにタイムアウトします。一呼吸おいて数回リトライしてください
鍵に埋め込まれた Notation を golang で読み込むには以下の記事をご覧ください。
TS; DR (検証してみる)
ここで、上記で生成された公開鍵を golang 以外の実装で読み込めるか検証したいと思ったのですが、重大な事実が発覚。
なんと、天下の GnuPG は RFC9580 に反対しており、この(RFC9580, OpenGPG v6)鍵は GnuPG では使えません。
そこで、RFC9580 の Rust による実装である Sequoia PGP(sq
コマンド)で読み込めるか検証してみたいと思います。
まずは、鍵がちゃんと生成されているかの lint
テストです。
$ sq cert lint --cert-file testdata/pubKey_RFC9580_w_notations.asc
Examined 1 certificate.
0 certificates are invalid and were not linted. (GOOD)
1 certificate was linted.
0 of the 1 certificates (0%) have at least one issue. (GOOD)
0 of the linted certificates were revoked.
0 of the 0 certificates has revocation certificates that are weaker than the certificate and should be recreated. (GOOD)
0 of the linted certificates were expired.
1 of the non-revoked linted certificate has at least one
non-revoked User ID:
0 have at least one User ID protected by SHA-1. (GOOD)
0 have all User IDs protected by SHA-1. (GOOD)
1 of the non-revoked linted certificates has at least one
non-revoked, live subkey:
0 have at least one non-revoked, live subkey with a binding signature that uses SHA-1. (GOOD)
0 of the non-revoked linted certificates have at least one
non-revoked, live, signing-capable subkey:
0 certificates have at least one non-revoked, live, signing-capable subkey with a strong binding signature, but a backsig that uses SHA-1. (GOOD)
すべてが (GOOD)
なので、鍵としては機能している(読み込める)ようです。
次に、鍵の情報を取得してみたいと思います。
以下の2行(Notation)が、署名パケット(Signature Packet
)に各々埋め込まれれていることを確認してください。
Notation: ariadne.id: dns:example.com
Notation: ariadne.id: https://blog.example.com/
$ sq packet dump testdata/pubKey_RFC9580_w_notations.asc
Public-Key Packet, new CTB, 67 bytes
Version: 6
Creation time: 2025-06-28 00:42:36 UTC
Pk algo: Ed448
Pk size: 456 bits
Fingerprint: 2E381AB387334AA0A381957499A4AA7B728D074FFE4592DB28E36B56A8D8D1AB
KeyID: 2E381AB387334AA0
Signature Packet, new CTB, 312 bytes
Version: 6
Type: DirectKey
Pk algo: Ed448
Hash algo: SHA512
Hashed area:
Signature creation time: 2025-06-28 00:42:36 UTC (critical)
Symmetric algo preferences: AES256, AES128
Notation: ariadne.id: dns:example.com
Notation: ariadne.id: https://blog.example.com/
Hash preferences: SHA512, SHA256
Compression preferences: Uncompressed, Zlib
Key flags: CS (critical)
Features: SEIPDv1, SEIPDv2
Issuer Fingerprint: 2E381AB387334AA0A381957499A4AA7B728D074FFE4592DB28E36B56A8D8D1AB (critical)
AEAD preferences: AES256+OCB, AES128+OCB
Digest prefix: A71C
Salt: 55DDEEDDBD719A981BD69F58E1D5847354C2DF4C97552AEB5BF8AABBBD455B12
Level: 0 (signature over data)
User ID Packet, new CTB, 23 bytes
Value: myName <my@example.com>
Signature Packet, new CTB, 291 bytes
Version: 6
Type: PositiveCertification
Pk algo: Ed448
Hash algo: SHA512
Hashed area:
Signature creation time: 2025-06-28 00:42:36 UTC (critical)
Notation: ariadne.id: dns:example.com
Notation: ariadne.id: https://blog.example.com/
Primary User ID: true
Issuer Fingerprint: 2E381AB387334AA0A381957499A4AA7B728D074FFE4592DB28E36B56A8D8D1AB (critical)
Digest prefix: 37DE
Salt: FB98200CD8C2F0CF5B277E379906DEE0DC16C99E22D11A3C0439B24FC8C724C6
Level: 0 (signature over data)
Public-Subkey Packet, new CTB, 66 bytes
Version: 6
Creation time: 2025-06-28 00:42:36 UTC
Pk algo: X448
Pk size: 448 bits
Fingerprint: 55DB4AB4E72CD0F1D1EB0FF40010E47A77CD57F3ECF8F38C2644ADED8B3222E8
KeyID: 55DB4AB4E72CD0F1
Signature Packet, new CTB, 291 bytes
Version: 6
Type: SubkeyBinding
Pk algo: Ed448
Hash algo: SHA512
Hashed area:
Signature creation time: 2025-06-28 00:42:36 UTC (critical)
Notation: ariadne.id: dns:example.com
Notation: ariadne.id: https://blog.example.com/
Key flags: EtEr (critical)
Issuer Fingerprint: 2E381AB387334AA0A381957499A4AA7B728D074FFE4592DB28E36B56A8D8D1AB (critical)
Digest prefix: B400
Salt: B6DEBB6E85516FA7AD60D10DC9B40CC518076693287C6A3B134D04F2892E3AED
Level: 0 (signature over data)