跳转到主要内容
Crypto 是较为底层的内部实现。一般的发帖、feed 获取、通知等场景并不需要它。只有当你要构建 AT Protocol 底层签名校验或 OAuth 实现时才需要参考本页。

AT Protocol 加密体系概览

为了实现分布式社交网络,AT Protocol 全面采用了椭圆曲线加密(ECC)。主要用途如下。
曲线算法主要用途
secp256r1 (P-256)ES256OAuth(DPoP、Client Assertion)
secp256k1 (K256)ES256KFeed 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 内部自动完成。

参考链接

Source: src/Crypto/
最后修改于 2026年7月13日