Crypto 是较为底层的内部实现。一般的发帖、feed 获取、通知等场景并不需要它。只有当你要构建 AT Protocol 底层签名校验或 OAuth 实现时才需要参考本页。
AT Protocol 加密体系概览
为了实现分布式社交网络,AT Protocol 全面采用了椭圆曲线加密(ECC)。主要用途如下。
| 曲线 | 算法 | 主要用途 |
|---|
| secp256r1 (P-256) | ES256 | OAuth(DPoP、Client Assertion) |
| secp256k1 (K256) | ES256K | Feed Generator、Labeler 的签名校验 |
密钥对类
AbstractKeypair
AbstractKeypair 是 P256 / K256 共同的基类。内部使用 phpseclib3。
// 生成新的密钥对
$keypair = P256::create();
$keypair = K256::create();
// 从 URL-safe Base64 编码的私钥加载
$keypair = P256::load($base64PrivateKey);
// 获取 PEM 格式
$privatePem = $keypair->privatePEM();
$publicPem = $keypair->publicPEM();
// 转换为 JWK (JSON Web Key)
$jwk = $keypair->toJWK();
P256 (secp256r1)
use Revolution\Bluesky\Crypto\P256;
$keypair = P256::create();
在 OAuth(DPoP / Client Assertion)中使用。OAuthKey 继承自 P256,会从 config('bluesky.oauth.private_key') 加载私钥。
包中也提供了用于生成新 OAuth 私钥的 Artisan 命令。
php artisan bluesky:new-private-key
K256 (secp256k1)
use Revolution\Bluesky\Crypto\K256;
$keypair = K256::create();
用于 Feed Generator 和 Labeler 的认证。Bluesky 的 PDS / Relay 会使用此曲线校验签名。
DidKey
DidKey 类负责将公钥在 did:key 格式之间进行编解码。
什么是 did:key 格式
AT Protocol 用形如 did:key:z... 的字符串来表示公钥。它是在公钥之前加上曲线标识后,再用 Base58btc 做 multibase 编码得到的。
将公钥编码为 did:key
use Revolution\Bluesky\Crypto\DidKey;
use Revolution\Bluesky\Crypto\K256;
$keypair = K256::create();
$publicPem = $keypair->publicPEM();
// 转为 did:key 格式
$didKey = DidKey::format($publicPem);
// => "did:key:zQ3s..."
从 DID Document 解析公钥
use Revolution\Bluesky\Crypto\DidKey;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Support\DidDocument;
$didDoc = DidDocument::make(
Bluesky::identity()->resolveDID('did:plc:***')->json()
);
// 从 DID Document 的 publicKey (multibase) 生成 phpseclib3 的公钥对象
$publicKey = DidKey::parse($didDoc->publicKey());
JsonWebToken (JWT)
JsonWebToken 类提供了 JWT 的编解码。
use Revolution\Bluesky\Crypto\JsonWebToken;
// 生成 JWT(用 PEM 私钥签名)
$token = JsonWebToken::encode(
payload: ['iss' => 'did:plc:***', 'aud' => 'https://bsky.social', 'exp' => time() + 60],
key: $privatePem,
alg: 'ES256',
);
// 解码 JWT(不做签名校验)
$payload = JsonWebToken::decode($token);
DPoP (Demonstrated Proof of Possession)
DPoP 是一种把 OAuth access token 绑定到特定客户端密钥对上的安全机制,用于防止 token 被重放攻击。
DPoP 类是内部使用的,通常不需要直接调用。OAuthAgent 中间件会自动附加 DPoP header。
// 生成 DPoP proof(用于 OAuth token 请求)
$proof = DPoP::authProof(
jwk: $jsonWebKey,
url: 'https://bsky.social/oauth/token',
method: 'POST',
nonce: $nonce,
);
// 生成 DPoP proof(用于 API 请求,包含 ath)
$proof = DPoP::apiProof(
jwk: $jsonWebKey,
url: 'https://api.bsky.app/xrpc/...',
method: 'GET',
token: $accessToken,
nonce: $nonce,
);
Signature(签名格式转换)
AT Protocol 使用 64 字节的紧凑签名格式,而 phpseclib3 返回的是 ASN.1 DER 格式。Signature 类负责它们之间的转换。
use Revolution\Bluesky\Crypto\Signature;
// ASN.1 DER → compact(64 字节)
$compact = Signature::toCompact($derSignature);
// compact → ASN.1 DER
$der = Signature::fromCompact($compactSignature);
JsonWebKey
JsonWebKey 是表示 JWK (JSON Web Key) 的类,用于生成 DPoP proof。
use Revolution\Bluesky\Crypto\JsonWebKey;
// 从密钥对生成 JWK
$jwk = $keypair->toJWK();
// 或者直接实例化
$jwk = JsonWebKey::load(['kty' => 'EC', 'crv' => 'P-256', ...]);
OAuthKey
OAuthKey 是继承自 P256、专门用于 OAuth 的密钥类,会将 config('bluesky.oauth.private_key') 的值作为私钥使用。
use Revolution\Bluesky\Crypto\OAuthKey;
// 从配置中加载私钥
$oauthKey = OAuthKey::load();
// 用于对 Client Assertion JWT 进行签名
$jwk = $oauthKey->toJWK();
一般情况下,这些操作都会由 Bluesky Socialite 内部自动完成。
参考链接