在给博客加上 GPG 数字签名后,我开始思考一个问题:如何让读者更优雅地获取我的公钥?

传统的做法是把公钥上传到 Keyserver,或者直接贴个下载链接。但最极客的做法是支持 Web Key Directory (WKD) 协议。

简单来说,配置好 WKD 后,任何人只需在终端输入:

gpg --locate-key [email protected]

GPG 就会自动去我的域名下寻找并下载公钥。这不仅酷,更是将“身份”与“域名”进行了强绑定。

本文记录了在 Hugo + Cloudflare Pages 架构下的部署流程,以及在 Windows 环境下遇到的坑。

🛠️ 前置:解决 Windows Git Bash 的 GPG 冲突

在 Windows 上使用 Git Bash 时,最头疼的问题就是它自带了一个简版 GPG,无法读取安装在系统(Gpg4win/Kleopatra)里的密钥。

导致的结果就是:git commit -S 报错,或者运行脚本时提示 No secret key

1. 强制 Git 使用系统 GPG

告诉 Git 不要用自带的,而是去调用 Gpg4win:

# 请根据实际安装位置修改路径
git config --global gpg.program "E:\Program Files (x86)\GnuPG\bin\gpg.exe"

2. 强制终端使用系统 GPG

为了在 Git Bash 命令行里直接敲 gpg 时也能调用系统程序,需要修改 ~/.bashrc

# 编辑配置文件
code ~/.bashrc

# 在末尾加入别名
alias gpg='/e/Program\ Files\ \(x86\)/GnuPG/bin/gpg.exe'

保存后运行 source ~/.bashrc 生效。现在,环境终于打通了。


🟢 方法一:手动部署 (命令行流)

如果你不常更换密钥,手动生成一次是最快的方法。WKD 的原理其实就是在网站特定的目录下,放一个以 哈希值 命名的公钥文件。

1. 创建目录结构

Hugo 的静态文件放在 static 目录下:

mkdir -p static/.well-known/openpgp/wkd/hu

2. 获取 WKD 哈希值

WKD 使用 Z-Base32 算法对邮箱进行哈希。GPG 自带了计算功能:

gpg --list-keys --with-wkd-hash [email protected]

输出中 wkd/hu/ 后面那串 32位字符 就是我们要的文件名(例如 4n9x8k2d7fp1a3qy5j6h4bv0c8mz2s5r)。

3. 导出二进制公钥

注意:WKD 要求公钥必须是 二进制 (Binary) 格式,不能是 ASCII Armor(即不能包含 BEGIN PGP…)。

# 语法:gpg --no-armor --export 邮箱 > 目标路径/哈希文件名
gpg --no-armor --export [email protected] > static/.well-known/openpgp/wkd/hu/4n9x8k2d7fp1a3qy5j6h4bv0c8mz2s5r

4. 创建策略文件

协议要求存在一个空的 policy 文件:

touch static/.well-known/openpgp/wkd/policy

部署到 Cloudflare 后,WKD 即刻生效。


🐍 方法二:Python 脚本自动化 (DevOps 流)

作为一个工科男,我更喜欢把这个过程脚本化,集成到博客的工具箱里。

我编写了一个 Python 脚本,它会自动寻找系统 GPG 路径,计算哈希,并导出文件。

脚本路径_tools/gen_wkd.py

import os
import subprocess
import sys

# === 配置区域 ===
TARGET_EMAIL = "[email protected]"

# 路径锚点
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_ROOT = os.path.dirname(SCRIPT_DIR)
WKD_BASE_DIR = os.path.join(PROJECT_ROOT, 'static', '.well-known', 'openpgp', 'wkd')
HU_DIR = os.path.join(WKD_BASE_DIR, 'hu')

# 自动寻找系统 GPG 路径 (兼容多盘符安装)
def find_gpg():
    candidates = [
        r"E:\Program Files (x86)\GnuPG\bin\gpg.exe",
        r"C:\Program Files (x86)\GnuPG\bin\gpg.exe",
        "gpg"
    ]
    for path in candidates:
        if os.path.exists(path): return path
    return "gpg"

GPG_EXE = find_gpg()

def main():
    # 强制 UTF-8 输出,防止 Windows 终端乱码
    sys.stdout.reconfigure(encoding='utf-8')
    print(f"🔐 开始生成 WKD 配置 (针对: {TARGET_EMAIL})...")

    if not os.path.exists(HU_DIR):
        os.makedirs(HU_DIR)

    # 创建 policy 文件
    policy_path = os.path.join(WKD_BASE_DIR, 'policy')
    if not os.path.exists(policy_path):
        open(policy_path, 'w').close()

    try:
        # 1. 获取哈希
        cmd = [GPG_EXE, "--list-keys", "--with-wkd-hash", TARGET_EMAIL]
        result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
        
        # 正则提取哈希
        import re
        match = re.search(r'wkd/hu/([a-z0-9]{32})', result.stdout)
        if not match:
            print("❌ 未找到 WKD 哈希,请检查邮箱或 GPG 版本。")
            return
            
        wkd_hash = match.group(1)
        print(f"   🔍 哈希值: {wkd_hash}")

        # 2. 导出公钥
        output_file = os.path.join(HU_DIR, wkd_hash)
        export_cmd = [GPG_EXE, "--no-armor", "--export", "--output", output_file, TARGET_EMAIL]
        subprocess.run(export_cmd, check=True)
        
        print(f"   ✅ WKD 配置已生成至: static/.../hu/{wkd_hash}")

    except Exception as e:
        print(f"❌ 执行出错: {e}")

if __name__ == "__main__":
    main()

验证

网站部署完成后,在任何一台安装了 GPG 的电脑上运行:

gpg --locate-key [email protected]

如果看到公钥被成功导入,说明你也拥有了自己的 Web Key Directory

这不仅仅是一个技术配置,更是去中心化网络身份认证的重要一环。


💡 维护贴士

  • 如果你更新了密钥(比如延长了有效期、添加了新子钥),记得重新运行一次导出命令(手动或脚本),并重新部署博客。