定制 skillmonster/http-message-signature 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

skillmonster/http-message-signature

Composer 安装命令:

composer require skillmonster/http-message-signature

包简介

PHP implementation of RFC 9421 HTTP Message Signatures

README 文档

README

PHP Version License

A complete PHP implementation of RFC 9421: HTTP Message Signatures, providing cryptographic signing and verification of HTTP messages.

Features

  • Full RFC 9421 Compliance - Complete implementation of the HTTP Message Signatures standard
  • 🔐 Multiple Algorithms - Support for RSA-PSS-SHA512, RSA-PKCS1-v1.5-SHA256, ECDSA-P256-SHA256, HMAC-SHA256, and Ed25519
  • 📦 PSR-7 Compatible - Works with any PSR-7 HTTP message implementation
  • 🎯 Derived Components - Support for @method, @target-uri, @authority, @path, @query, @status, and more
  • 🔄 Multiple Signatures - Add multiple signatures to a single message
  • 🔑 Flexible Key Formats - Supports PEM, DER, base64, and raw key formats
  • 🛡️ Content Digest - SHA-512 content digest support for body integrity
  • 🎲 Nonce Support - Built-in random nonce generation for replay protection
  • 🏷️ Tag Parameter - Application-specific tag support
  • Easy to Use - Simple, intuitive API
  • 🧪 Well Tested - Comprehensive test suite
  • 📝 Type Safe - Full PHP 8.1+ type declarations
  • 🚀 Production Ready - Battle-tested with real-world APIs

Installation

Install via Composer:

composer require skillmonster/http-message-signature

Requirements

  • PHP 8.1 or higher
  • OpenSSL extension
  • JSON extension
  • Sodium extension (for Ed25519 support, included in PHP 7.2+)
  • PSR-7 HTTP Message implementation (e.g., guzzlehttp/psr7)

Quick Start

Basic HMAC Signature

<?php

use HttpSignature\HttpSignature;
use HttpSignature\Algorithm\HmacSha256;
use GuzzleHttp\Psr7\Request;

// Create a PSR-7 request
$request = new Request(
    'POST',
    'https://api.example.com/users',
    [
        'Host' => 'api.example.com',
        'Content-Type' => 'application/json'
    ],
    '{"hello":"world"}'
);

// Initialize the signature handler
$httpSignature = new HttpSignature();
$algorithm = new HmacSha256();
$sharedSecret = 'your-secret-key';

// Sign the request
$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $algorithm,
    privateKey: $sharedSecret,
    componentNames: ['@method', '@path', '@authority', 'content-type']
);

// Verify the signature
$isValid = $httpSignature->verify(
    message: $signedRequest,
    algorithm: $algorithm,
    publicKey: $sharedSecret
);

echo $isValid ? "✓ Signature valid" : "✗ Signature invalid";

RSA Signature

<?php

use HttpSignature\HttpSignature;
use HttpSignature\Algorithm\RsaPssSha512;
use GuzzleHttp\Psr7\Request;

// Load your RSA keys
$privateKey = file_get_contents('/path/to/private-key.pem');
$publicKey = file_get_contents('/path/to/public-key.pem');

$request = new Request('GET', 'https://api.example.com/data', [
    'Host' => 'api.example.com'
]);

$httpSignature = new HttpSignature();
$algorithm = new RsaPssSha512();

// Sign with RSA
$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $algorithm,
    privateKey: $privateKey,
    componentNames: ['@method', '@authority', '@path'],
    additionalParams: [
        'keyid' => 'my-rsa-key',
        'expires' => time() + 300 // 5 minutes
    ]
);

// Verify
$isValid = $httpSignature->verify(
    message: $signedRequest,
    algorithm: $algorithm,
    publicKey: $publicKey
);

Supported Algorithms

Algorithm Class RFC 9421 Section Key Formats Notes
Ed25519 Ed25519 Modern PEM (PKCS#8), base64, raw ✅ Recommended - Fast & secure
RSA-PSS-SHA512 RsaPssSha512 3.3.1 PEM, DER, base64 Requires OpenSSL
RSA-PKCS1-v1.5-SHA256 RsaPkcs1v15Sha256 3.3.2 PEM, DER, base64 Requires OpenSSL
ECDSA-P256-SHA256 EcdsaP256Sha256 3.3.3 PEM, DER, base64 Requires OpenSSL
HMAC-SHA256 HmacSha256 3.3.4 Raw string Built-in, shared secret

Algorithm Selection Guide

  • Ed25519: Best choice for modern APIs. Fast, secure, small signatures (64 bytes)
  • RSA-PSS-SHA512: Good for compatibility with existing RSA infrastructure
  • ECDSA-P256-SHA256: Smaller keys than RSA, good performance
  • HMAC-SHA256: Simple shared secret, good for internal services

Ed25519 Example (Recommended)

Ed25519 is the recommended algorithm for modern APIs due to its speed, security, and small signature size.

Using Ed25519 with PEM Keys

use HttpSignature\Algorithm\Ed25519;

// Load PEM format keys (PKCS#8)
$privateKey = file_get_contents('private-key.pem');
$publicKey = file_get_contents('public-key.pem');

// Sign with Ed25519
$algorithm = new Ed25519();
$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $algorithm,
    privateKey: $privateKey,
    componentNames: [
        '@method',
        '@target-uri',
        'content-type',
        'content-digest',
        'accept'
    ],
    additionalParams: [
        'keyid' => 'my-ed25519-key',
        'alg' => 'ed25519',
        'tag' => 'payment',
        'nonce' => base64_encode(random_bytes(32)),
        'created' => time()
    ]
);

// Verify
$isValid = $httpSignature->verify($signedRequest, $algorithm, $publicKey);

Generate Keypair Programmatically

use HttpSignature\Algorithm\Ed25519;

// Generate new keypair
$keypair = Ed25519::generateKeypair();

// Keys are base64-encoded
$privateKeyB64 = $keypair['privateKey'];
$publicKeyB64 = $keypair['publicKey'];

// Decode for use
$privateKey = base64_decode($privateKeyB64);
$publicKey = base64_decode($publicKeyB64);

Component Identifiers

HTTP Fields

Sign any HTTP header by using its lowercase name:

['content-type', 'date', 'authorization']

Derived Components

Special components derived from the HTTP message:

Component Description Example Value
@method HTTP method GET, POST
@target-uri Full request URI https://example.com/path?query=value
@authority Host and port example.com:443
@scheme URI scheme https
@request-target Request target /path?query=value
@path URI path /path
@query Query string ?query=value
@status Response status code 200

Advanced Usage

Multiple Signatures

Add multiple signatures to a single message:

$request = new Request('GET', 'https://example.com/data', [
    'Host' => 'example.com'
]);

// Add first signature
$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $hmacAlgorithm,
    privateKey: $secret1,
    componentNames: ['@method', '@path'],
    signatureName: 'sig1'
);

// Add second signature
$signedRequest = $httpSignature->addSignature(
    message: $signedRequest,
    algorithm: $rsaAlgorithm,
    privateKey: $rsaPrivateKey,
    componentNames: ['@authority', 'date'],
    signatureName: 'sig2'
);

// Verify each signature
$valid1 = $httpSignature->verify($signedRequest, $hmacAlgorithm, $secret1, 'sig1');
$valid2 = $httpSignature->verify($signedRequest, $rsaAlgorithm, $rsaPublicKey, 'sig2');

Signature Parameters

Add metadata to your signatures:

$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $algorithm,
    privateKey: $privateKey,
    componentNames: ['@method', '@target-uri', 'content-type'],
    additionalParams: [
        'keyid' => 'my-key-identifier',           // Key identifier
        'alg' => 'ed25519',                        // Algorithm name
        'tag' => 'payment',                        // Application-specific tag
        'nonce' => base64_encode(random_bytes(32)), // Random nonce (32 bytes)
        'created' => time(),                       // Creation timestamp
        'expires' => time() + 300                  // Expiration timestamp (5 min)
    ]
);

Available Parameters

Parameter Type Description Example
keyid string Key identifier "my-key-2024"
alg string Algorithm name "ed25519"
tag string Application tag "payment", "webhook"
nonce string Random nonce Base64-encoded random bytes
created int Creation timestamp time()
expires int Expiration timestamp time() + 300

Query Parameters

Sign specific query parameters:

use HttpSignature\Component\ComponentIdentifier;

$components = [
    new ComponentIdentifier('@method'),
    new ComponentIdentifier('@query-param', ['name' => 'user_id'])
];

Content Digest

Include body integrity verification using SHA-512 content digest:

// Generate content digest
$body = json_encode(['amount' => 1000, 'currency' => 'LAK']);
$hash = hash('sha512', $body, true);
$contentDigest = 'sha-512=:' . base64_encode($hash) . ':';

// Create request with digest
$request = new Request(
    'POST',
    'https://api.example.com/payment',
    [
        'Content-Type' => 'application/json',
        'Content-Digest' => $contentDigest,
        'Accept' => 'application/json'
    ],
    $body
);

// Sign including the digest
$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $algorithm,
    privateKey: $privateKey,
    componentNames: [
        '@method',
        '@target-uri',
        'content-type',
        'content-digest',  // Include digest in signature
        'accept'
    ]
);

Real-World Example: Payment API

Complete example of signing a payment API request with Ed25519:

<?php

use HttpSignature\HttpSignature;
use HttpSignature\Algorithm\Ed25519;
use GuzzleHttp\Psr7\Request;

// Configuration
$privateKey = file_get_contents('private-key.pem');
$keyId = 'your-key-id';

// Payment data
$paymentData = [
    'mid' => 'MERCHANT123',
    'amount' => '50000',
    'currency' => 'LAK',
    'billId' => 'ORDER-' . time(),
    'customer' => [
        'name' => 'John Doe',
        'email' => 'john@example.com'
    ]
];

// Create request body and digest
$jsonBody = json_encode($paymentData);
$contentDigest = 'sha-512=:' . base64_encode(hash('sha512', $jsonBody, true)) . ':';
$nonce = base64_encode(random_bytes(32));

// Create PSR-7 request
$request = new Request(
    'POST',
    'https://api.payment.com/v2/payments/create',
    [
        'Content-Type' => 'application/json',
        'Content-Length' => (string) strlen($jsonBody),
        'Content-Digest' => $contentDigest,
        'Accept' => 'application/json'
    ],
    $jsonBody
);

// Sign the request
$httpSignature = new HttpSignature();
$algorithm = new Ed25519();

$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: $algorithm,
    privateKey: $privateKey,
    componentNames: [
        '@method',
        '@target-uri',
        'content-type',
        'content-length',
        'content-digest',
        'accept'
    ],
    additionalParams: [
        'keyid' => $keyId,
        'alg' => 'ed25519',
        'tag' => 'payment',
        'nonce' => $nonce,
        'created' => time()
    ]
);

// Send with your HTTP client
$client = new \GuzzleHttp\Client();
$response = $client->send($signedRequest);

echo "Payment created: " . $response->getBody();

Message Format

When a message is signed, two headers are added:

Signature-Input Header

Contains the signature metadata and covered components:

Signature-Input: sig1=("@method" "@path" "content-type");created=1618884473;keyid="test-key"

Signature Header

Contains the actual signature value:

Signature: sig1=:K2qGT5srn2OGbOIDzQ6kYT+ruaycnDAAUpKv+ePFfD6UFh1L1EwBs=:

Security Considerations

What to Sign

Always include components that are critical to your application's security:

For Requests (Recommended):

  • @method - Prevent method confusion attacks
  • @target-uri - Prevent request redirection (includes full URL)
  • content-type - Prevent content type confusion
  • content-length - Verify body length
  • content-digest - Ensure body integrity (SHA-512 recommended)
  • accept - Specify expected response format

Alternative (Legacy):

  • @method, @authority, @path - If @target-uri not supported

For Responses:

  • @status - Verify response status
  • content-type - Verify content type
  • content-digest - Verify response body integrity
  • Critical business headers

Recommended Signature Configuration

// Production-ready configuration
$signedRequest = $httpSignature->sign(
    message: $request,
    algorithm: new Ed25519(), // Recommended algorithm
    privateKey: $privateKey,
    componentNames: [
        '@method',
        '@target-uri',      // Full URL protection
        'content-type',
        'content-length',
        'content-digest',   // Body integrity
        'accept'
    ],
    additionalParams: [
        'keyid' => $keyId,
        'alg' => 'ed25519',
        'tag' => 'payment',                        // Application context
        'nonce' => base64_encode(random_bytes(32)), // Replay protection
        'created' => time(),
        'expires' => time() + 300                  // 5 minutes
    ]
);

Key Management

  • Use Ed25519 for new implementations (fast, secure, small keys)
  • Use strong, randomly generated keys
  • Rotate keys regularly (every 90 days recommended)
  • Store private keys securely (HSM, key vault, environment variables)
  • Never hardcode keys in source code
  • Use different keys for different environments (dev, staging, prod)
  • Share only public keys with API providers

Signature Expiration

Always set an expiration time to prevent replay attacks:

'expires' => time() + 300 // 5 minutes for high-security APIs
'expires' => time() + 3600 // 1 hour for less sensitive operations

Replay Protection

Use nonces to prevent replay attacks:

// Generate 32-byte random nonce
'nonce' => base64_encode(random_bytes(32))

Server-side: Track used nonces within the signature validity window.

Content Digest

Always include content digest for POST/PUT requests:

$body = json_encode($data);
$hash = hash('sha512', $body, true);
$contentDigest = 'sha-512=:' . base64_encode($hash) . ':';

This ensures the request body hasn't been tampered with.

Testing

Run the test suite:

composer test

Run static analysis:

composer phpstan

Check code style:

composer cs-check

Examples

See the examples/ directory for complete working examples:

Basic Examples

  • basic_usage.php - HMAC signatures and multiple signatures
  • rsa_signature.php - RSA signatures and tampering detection

Ed25519 Examples

  • ed25519_signature.php - Ed25519 signature basics
  • ed25519_with_existing_key.php - Using existing Ed25519 keys
  • ed25519_http_request.php - Complete HTTP request examples
  • generate_ed25519_keys.php - Generate Ed25519 keypairs

Real-World Integration

  • uniqpay_laoqr_example.php - Complete UniqPay LaoQR payment API integration
  • UniqPayClient.php - Production-ready payment API client class

Run examples:

# Basic examples
php examples/basic_usage.php
php examples/rsa_signature.php

# Ed25519 examples
php examples/ed25519_signature.php
php examples/generate_ed25519_keys.php

# Real-world payment API
php examples/uniqpay_laoqr_example.php

UniqPay Integration Example

The library includes a complete, production-ready integration with UniqPay's LaoQR payment API:

require_once 'examples/UniqPayClient.php';

$client = new UniqPayClient(
    privateKeyPath: 'private-key.pem',
    keyId: 'your-key-id',
    mid: 'YOUR_MERCHANT_ID',
    recvId: 'YOUR_RECEIVER_ID',
    httpClient: new \GuzzleHttp\Client()
);

// Create payment
$result = $client->createPayment([
    'billId' => 'ORDER-12345',
    'terminalId' => '000',
    'purpose' => 'Payment for goods',
    'amount' => '50000',
    'currency' => 'LAK',
    'customer' => [
        'id' => 'C001',
        'fullname' => 'John Doe',
        'email' => 'john@example.com',
        'phone' => '+8562012345678'
    ]
]);

if ($result['success']) {
    $payment = $result['data']['data'];
    echo "Payment ID: " . $payment['id'] . "\n";
    echo "LaoQR Code: " . $payment['laoQr'] . "\n";
}

RFC 9421 Compliance

This library implements the following sections of RFC 9421:

  • ✅ Section 2: HTTP Message Components
    • 2.1: HTTP Fields
    • 2.2: Derived Components
    • 2.3: Signature Parameters
    • 2.5: Creating the Signature Base
  • ✅ Section 3: HTTP Message Signatures
    • 3.1: Creating a Signature
    • 3.2: Verifying a Signature
    • 3.3: Signature Algorithms
  • ✅ Section 4: Including a Message Signature in a Message
    • 4.1: The Signature-Input HTTP Field
    • 4.2: The Signature HTTP Field
    • 4.3: Multiple Signatures

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

git clone https://github.com/skillmonster/http-message-signature.git
cd http-message-signature
composer install
composer test

License

This library is licensed under the MIT License. See the LICENSE file for details.

References

Support

Made with ❤️ by Vanvixay Thammavong

统计信息

  • 总下载量: 3
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-10-13