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 のペア鍵を生成する【GopenPGP v3 + RFC9580】

Last updated at Posted at 2025-06-27

Go で RFC9580 準拠の OpenPGP 鍵ペアを作成する

Go 言語(以下 golang)で、GopenPGP v3 モジュールを使って RFC9580 に準拠した OpenPGP(パケット v6)鍵を生成したい。

この記事は、以下の GopenPGP v2 で OpenPGP のペア鍵を生成する 記事のフォローアップ記事です。どうやら「最新の RFC9580 にすれば良い」というものではなさそうです(文末の TS; DR 参照)。

TL; DR 何はともあれ動くソースコード

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

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

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"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"

	// Create a PGP handler with the RFC9580 profile (OpenPGP v6 standard)
	// * Ref: https://www.ietf.org/rfc/rfc9580.pdf
	pgpHandler := crypto.PGPWithProfile(profile.RFC9580())

	// 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)
	ecKey, err := pgpGenerator.GenerateKeyWithSecurity(constants.HighSecurity)
	if err != nil {
		panic(err)
	}

	// Get the ASCII armored private key
	fmt.Println("Private Key (ASCII Armored):")
	privKeyAsc, err := ecKey.Armor()
	if err != nil {
		panic(err)
	}

	fmt.Println(privKeyAsc)

	// Get the ASCII armored public key
	fmt.Println("Public Key (ASCII Armored):")
	pubKeyAsc, err := ecKey.GetArmoredPublicKey()
	if err != nil {
		panic(err)
	}

	fmt.Println(pubKeyAsc)

	// Save the public key to a file
	pathFileKey := filepath.Join("..", "testdata", "pubKey.asc")
	if err := os.WriteFile(pathFileKey, []byte(pubKeyAsc), 0644); err != nil {
		panic(err)
	}
}

生成された鍵の検証

上記で生成された OpenPGP 鍵の公開鍵が「他のアプリから読み込めるか」検証してみたいと思いました。

ところが、GnuPG(v2.4.8 現在)では、RFC9580(OpenPGP の v6)には公式対応していませんでした(その理由は後述)。

GnuPG で検証(未対応)
$ gpg --list-packets ./test_pubKey.asc
gpg: packet(6) with unknown version 6
gpg: packet(2) with unknown version 6
gpg: packet(2) with unknown version 6
gpg: packet(14) with unknown version 6
gpg: packet(2) with unknown version 6
gpg: 有効なOpenPGPデータが見つかりません。
# off=0 ctb=c6 tag=6 hlen=2 plen=67 new-ctb
:key packet: [unknown version]
# off=69 ctb=c2 tag=2 hlen=3 plen=226 new-ctb
:signature packet: [unknown version]
# off=298 ctb=cd tag=13 hlen=2 plen=23 new-ctb
:user ID packet: "myName <my@example.com>"
# off=323 ctb=c2 tag=2 hlen=3 plen=205 new-ctb
:signature packet: [unknown version]
# off=531 ctb=ce tag=14 hlen=2 plen=66 new-ctb
:key packet: [unknown version]
# off=599 ctb=c2 tag=2 hlen=3 plen=205 new-ctb
:signature packet: [unknown version]

$ gpg --version
gpg (GnuPG) 2.4.8
libgcrypt 1.11.1
Copyright (C) 2025 g10 Code GmbH
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /Users/KEINOS/.gnupg
サポートしているアルゴリズム:
公開鍵: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
暗号方式: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256,
      TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256
ハッシュ: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
圧縮: 無圧縮, ZIP, ZLIB, BZIP2

OpenPGP v6 (RFC9580) に準拠したアプリでは、Rust で書かれた Sequoia PGP が対応していたので試してみました。

Sequoia PGP(sq)では、ちゃんと公開鍵を認識し、鍵情報も取得できました。

Sequoia PGP で検証
$ sq version
sq 1.3.1
using sequoia-openpgp 2.0.0
with cryptographic backend Nettle 3.10 (Cv448: true, OCB: true)

$ # 鍵内の証明書に関する統計情報を収集(構文チェック)
$ sq cert lint --cert-file ./test_pubKey.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)

$ # 鍵情報をダンプ出力
$ sq packet dump ./test_pubKey.asc
Public-Key Packet, new CTB, 67 bytes
    Version: 6
    Creation time: 2025-06-25 11:39:51 UTC
    Pk algo: Ed448
    Pk size: 456 bits
    Fingerprint: D2C036E970AD4CEE20F87B6E00A3F60B9711901C87BC7507058481455FFA8267
    KeyID: D2C036E970AD4CEE
  
Signature Packet, new CTB, 226 bytes
    Version: 6
    Type: DirectKey
    Pk algo: Ed448
    Hash algo: SHA512
    Hashed area:
      Signature creation time: 2025-06-25 11:39:51 UTC (critical)
      Symmetric algo preferences: AES256, AES128
      Hash preferences: SHA512, SHA256
      Compression preferences: Uncompressed, Zlib
      Key flags: CS (critical)
      Features: SEIPDv1, SEIPDv2
      Issuer Fingerprint: D2C036E970AD4CEE20F87B6E00A3F60B9711901C87BC7507058481455FFA8267 (critical)
      AEAD preferences: AES256+OCB, AES128+OCB
    Digest prefix: 35DD
    Salt: 83279F18B35CCBA007821B9A6B5F6769264C74BB7BB345EC2B9B8F32FEE3BB99
    Level: 0 (signature over data)
  
User ID Packet, new CTB, 23 bytes
    Value: myName <my@example.com>
  
Signature Packet, new CTB, 205 bytes
    Version: 6
    Type: PositiveCertification
    Pk algo: Ed448
    Hash algo: SHA512
    Hashed area:
      Signature creation time: 2025-06-25 11:39:51 UTC (critical)
      Primary User ID: true
      Issuer Fingerprint: D2C036E970AD4CEE20F87B6E00A3F60B9711901C87BC7507058481455FFA8267 (critical)
    Digest prefix: C759
    Salt: 7AC7EF086F75553A7EBCDA452E7644EDB167B058AC7F0C4A84156CF7CA130BDD
    Level: 0 (signature over data)
  
Public-Subkey Packet, new CTB, 66 bytes
    Version: 6
    Creation time: 2025-06-25 11:39:51 UTC
    Pk algo: X448
    Pk size: 448 bits
    Fingerprint: 90588080DC08E243F7B69483A2757BA48DC6362F8A519702A4578DC58956FF12
    KeyID: 90588080DC08E243
  
Signature Packet, new CTB, 205 bytes
    Version: 6
    Type: SubkeyBinding
    Pk algo: Ed448
    Hash algo: SHA512
    Hashed area:
      Signature creation time: 2025-06-25 11:39:51 UTC (critical)
      Key flags: EtEr (critical)
      Issuer Fingerprint: D2C036E970AD4CEE20F87B6E00A3F60B9711901C87BC7507058481455FFA8267 (critical)
    Digest prefix: 75F0
    Salt: 9606C0A4C711CF349BB9D3FC1152BBFDD9BFB4FF220323D8C866FD0E085AACDA
    Level: 0 (signature over data)

TS; DR

前回の記事と大きく違うのが、サポートする OpenPGP の仕様のバージョンです。

ながらくメインストリームにいた RFC4880(2007年公開)から、2025/06/26 現在、OpenPGP の最新の仕様は RFC9580(2024年7月公開)となりました。

RFC9580 の変更点はたくさんあるのですが、やはり RSA 鍵を明確に廃止(deprecated)と言い切ったことでしょう(RFC9580, 9.1 "Public Key Algorithms" 参照)。

しかし、調べてみて「これは大変だ」と思ったのが、天下の GnuPG は RFC9580 に反対しており、そもそも「RFC9580 には準拠しない」という方針に舵を切っていたことです。

というのも、RFC4880(v4)の改善提案として進められていた RFC4880bis(v5)に対し、この提案に大いに貢献していた GnuPG 開発者の意見を反映せずに、大幅な設計変更を加えた RFC9580 が制定されました。その結果、既存実装との互換性が薄れることを懸念した GnuPG は、RFC4880bis を基にした独自仕様フォークLibrePGP」を、自らの実装に反映しています。

RFC9580 の著者を見ると、GnuPG のメンバーが見当たらず、RFC9580 の実装に積極的な ProtonMailSequoia PGP のメンバーなのも、何か色々あったことが伺えます。

つまり「OpenPGP の実装」と「LibrePGP の実装」の、ダブルスタンダード(二大仕様)が生まれたことになります。

OpenPGP の実装と言えば GnuPG だったものが、そうではなくなったということです。しかも GnuPG に依存しているアプリは多いため、いやでも LibrePGP の仕様も網羅しないといけなくなったのが辛いところです。

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?