0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Golang】OpenPGP 鍵に Notation 情報を埋め込む【RFC9580, packet v6】

Last updated at Posted at 2025-06-28

Go 言語(以下 golang)で、RFC9580(packet v6)に準拠した OpenPGP 鍵を生成しつつ、鍵に Notation 情報も埋め込みたい

TL; DR (とりあえずソースコード)

まずは RFC9580 準拠の OpenPGP 鍵を生成するコードです。

ポイントは、単純に鍵を生成する方法と違い、プロファイル(profile.Custom)の SetKeyAlgorithm フィールド内の関数変数をオーバーライドしている箇所です。

また、この例ではセキュリティ・レベルを "High" に設定しています。そのため内部の楕円曲線方式が Ed25519Standard)から Ed448High)に強化されています。

  • 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 PGPsq コマンド)で読み込めるか検証してみたいと思います。

まずは、鍵がちゃんと生成されているかの 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)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?