在 Conan 2.2x.x 中,包签名(Package Signing)不再绑定于某种特定的工具,而是通过**插件化(Extension API)**实现的。因此可以使用 OpenSSL、GPG 甚至 Sigstore (Cosign) 来进行签名。
介绍
官方推荐通过在配置目录中编写 sign.py 插件来实现。当你在终端执行 conan cache sign 时,Conan 会生成一个包含该包所有文件校验和的清单文件 (pkgsign-manifest.json),然后调用你编写的 sign.py 对该清单文件进行签名。
注意:Package Signing 目前是 Conan 2 的**实验性(experimental)**特性,插件 API 与命令行为在后续版本中可能发生变更,生产使用前请以官方文档为准。
以下是一个可以直接在 Bash/终端 中运行的完整示例代码。它使用 OpenSSL 作为底层签名工具,自动生成密钥对、配置好 Conan 的签名插件,并最终对一个测试包进行签名验证。
一键执行脚本示例 (Bash)
请确保你的系统已经安装了 conan 和 openssl。
#!/bin/bash
# 1. 确保 Conan 插件存放目录存在
PLUGIN_DIR="$HOME/.conan2/extensions/plugins/sign"
ORG_DIR="$PLUGIN_DIR/my-organization"
mkdir -p "$ORG_DIR"
# 2. 生成 RSA 私钥和公钥 (仅做演示,存放在插件同级目录)
echo "=> 生成 OpenSSL 密钥对..."
openssl genpkey -algorithm RSA -out "$ORG_DIR/private_key.pem" -pkeyopt rsa_keygen_bits:2048
openssl pkey -in "$ORG_DIR/private_key.pem" -pubout -out "$ORG_DIR/public_key.pem"
# 3. 创建 Conan 的签名插件 sign.py
echo "=> 正在写入 Conan 签名插件..."
cat << 'EOF' > "$PLUGIN_DIR/sign.py"
import os
import json
import subprocess
from conan.errors import ConanException
from conan.api.output import ConanOutput
def sign(ref, artifacts_folder, signature_folder, **kwargs):
"""该函数用于为生成的包进行签名"""
manifest_filepath = os.path.join(signature_folder, "pkgsign-manifest.json")
signature_filepath = os.path.join(signature_folder, "pkgsign-manifest.json.sig")
# 获取存放在缓存目录里的私钥(生产环境中请替换为从环境变量/KMS服务读取)
privkey_filepath = os.path.join(os.path.dirname(__file__), "my-organization", "private_key.pem")
openssl_sign_cmd = [
"openssl", "dgst", "-sha256", "-sign", privkey_filepath,
"-out", signature_filepath, manifest_filepath
]
try:
subprocess.run(openssl_sign_cmd, check=True, capture_output=True)
ConanOutput().success(f"Package successfully signed: {ref}")
except subprocess.CalledProcessError as exc:
raise ConanException(f"Error signing artifact: {exc.stderr.decode()}")
# 将签名的元数据信息返回给 Conan 保存
return [{
"method": "openssl-dgst",
"provider": "my-organization",
"sign_artifacts": {
"manifest": "pkgsign-manifest.json",
"signature": "pkgsign-manifest.json.sig"
}
}]
def verify(ref, artifacts_folder, signature_folder, files, **kwargs):
"""该函数用于在下载/使用包前验证签名"""
signatures_filepath = os.path.join(signature_folder, "pkgsign-signatures.json")
if not os.path.exists(signatures_filepath):
raise ConanException(f"No signatures found for {ref}")
with open(signatures_filepath, "r") as f:
signatures = json.loads(f.read()).get("signatures", [])
pubkey_filepath = os.path.join(os.path.dirname(__file__), "my-organization", "public_key.pem")
for signature in signatures:
signature_filename = signature.get("sign_artifacts").get("signature")
manifest_filename = signature.get("sign_artifacts").get("manifest")
signature_filepath = os.path.join(signature_folder, signature_filename)
manifest_filepath = os.path.join(signature_folder, manifest_filename)
openssl_verify_cmd = [
"openssl", "dgst", "-sha256", "-verify", pubkey_filepath,
"-signature", signature_filepath, manifest_filepath
]
try:
subprocess.run(openssl_verify_cmd, check=True, capture_output=True)
ConanOutput().success(f"Signature correctly verified for: {ref}")
except subprocess.CalledProcessError as exc:
raise ConanException(f"Signature validation failed for {ref}: {exc.stderr.decode()}")
EOF
# 4. 创建一个用于测试用的虚拟包 (hello/1.0)
echo "=> 创建测试包 hello/1.0..."
mkdir -p /tmp/conan_hello_test && cd /tmp/conan_hello_test
conan new cmake_lib -d name=hello -d version=1.0
conan create .
# 5. 执行包签名(对 hello/1.0 及其所有 Revision 进行签名)
echo "=> 对缓存中的包执行签名命令..."
conan cache sign "hello/1.0:*"
# 6. 验证包签名
echo "=> 验证该包的签名..."
conan cache verify "hello/1.0:*"
echo "=> 演示完成!清理测试数据..."
rm -rf /tmp/conan_hello_test执行说明:
- 这个脚本会自动注入实现
sign()和verify()接口的 python 插件到~/.conan2/extensions/plugins/sign/sign.py中。 - 随后使用
conan new产生一个极简的演示库hello/1.0存储于你的 Local Cache 中。 conan cache sign "hello/1.0:*"是 Conan 2 新引入的缓存包签名标准命令,它将遍历本地缓存,为该包生成数字证书并将校验元数据打包写入缓存内部的metadata/sign目录中。conan cache verify "hello/1.0:*"则是用来校验防篡改的命令。
⚠️ 生产环境重要提示:
上述示例为了简化流程,将私钥直接储存在了 ~/.conan2 目录下。在真实的生产环境中,绝不要把私钥文件放在 Conan Cache 或代码仓库里。请通过调用环境变量读取私钥路径、对接 AWS KMS/HashiCorp Vault、或替换为类似 Sigstore(Cosign)这种 Keyless 等签名认证体系。
参考
- https://docs.conan.io/2/reference/extensions/package_signing.html
- https://docs.conan.io/2/reference/commands/cache.html