首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP PKI 加密技術(OpenSSL)完整實作指南

     編輯:PHP 相關知識

PKI 加密核心概念

模式加密鑰解密鑰用途
公鑰加密公鑰私鑰機密傳輸(僅持有私鑰者可解密)
私鑰加密私鑰公鑰數位簽章(驗證來源、防篡改)

OpenSSL 擴展啟用

; php.ini
extension=openssl
; Windows: extension=php_openssl.dll
; Linux: 通常內建或安裝 php-openssl

驗證:php -m | grep openssl 或 phpinfo() 搜尋 OpenSSL

金鑰對生成(OpenSSL CLI)

# 生成 RSA 2048 位元私鑰
openssl genrsa -out private.pem 2048

# 從私鑰導出公鑰
openssl rsa -in private.pem -pubout -out public.pem

# 查看金鑰資訊
openssl rsa -in private.pem -text -noout
openssl rsa -in public.pem -pubin -text -noout

PHP 完整實作類

<?php
/**
 * PKI 加密/簽章工具類
 */
class PkiCrypto {
    private $privateKey;
    private $publicKey;
    private $privateKeyResource;
    private $publicKeyResource;

    public function __construct(string $privateKeyPath = '', string $publicKeyPath = '') {
        if ($privateKeyPath) $this->loadPrivateKey($privateKeyPath);
        if ($publicKeyPath) $this->loadPublicKey($publicKeyPath);
    }

    public function loadPrivateKey(string $path, string $passphrase = ''): bool {
        $content = file_get_contents($path);
        if ($content === false) return false;
        $this->privateKey = $content;
        $this->privateKeyResource = openssl_pkey_get_private($content, $passphrase);
        return $this->privateKeyResource !== false;
    }

    public function loadPublicKey(string $path): bool {
        $content = file_get_contents($path);
        if ($content === false) return false;
        $this->publicKey = $content;
        $this->publicKeyResource = openssl_pkey_get_public($content);
        return $this->publicKeyResource !== false;
    }

    public function loadPrivateKeyFromString(string $pem, string $passphrase = ''): bool {
        $this->privateKey = $pem;
        $this->privateKeyResource = openssl_pkey_get_private($pem, $passphrase);
        return $this->privateKeyResource !== false;
    }

    public function loadPublicKeyFromString(string $pem): bool {
        $this->publicKey = $pem;
        $this->publicKeyResource = openssl_pkey_get_public($pem);
        return $this->publicKeyResource !== false;
    }

    // --- 公鑰加密 / 私鑰解密(機密傳輸) ---
    public function publicEncrypt(string $data): ?string {
        if (!$this->publicKeyResource) return null;
        $success = openssl_public_encrypt($data, $encrypted, $this->publicKeyResource, OPENSSL_PKCS1_PADDING);
        return $success ? $encrypted : null;
    }

    public function privateDecrypt(string $encrypted): ?string {
        if (!$this->privateKeyResource) return null;
        $success = openssl_private_decrypt($encrypted, $decrypted, $this->privateKeyResource, OPENSSL_PKCS1_PADDING);
        return $success ? $decrypted : null;
    }

    // --- 私鑰加密 / 公鑰解密(簽章驗證) ---
    public function privateEncrypt(string $data): ?string {
        if (!$this->privateKeyResource) return null;
        $success = openssl_private_encrypt($data, $encrypted, $this->privateKeyResource, OPENSSL_PKCS1_PADDING);
        return $success ? $encrypted : null;
    }

    public function publicDecrypt(string $encrypted): ?string {
        if (!$this->publicKeyResource) return null;
        $success = openssl_public_decrypt($encrypted, $decrypted, $this->publicKeyResource, OPENSSL_PKCS1_PADDING);
        return $success ? $decrypted : null;
    }

    // --- 數位簽章(推薦:SHA256 + 私鑰簽名,公鑰驗證) ---
    public function sign(string $data, string $algo = 'sha256'): ?string {
        if (!$this->privateKeyResource) return null;
        $success = openssl_sign($data, $signature, $this->privateKeyResource, $algo);
        return $success ? $signature : null;
    }

    public function verify(string $data, string $signature, string $algo = 'sha256'): int {
        if (!$this->publicKeyResource) return -1;
        return openssl_verify($data, $signature, $this->publicKeyResource, $algo);
        // 返回 1=成功, 0=失敗, -1=錯誤
    }

    // --- 方便方法:Base64 編碼輸出 ---
    public function publicEncryptBase64(string $data): ?string {
        $enc = $this->publicEncrypt($data);
        return $enc ? base64_encode($enc) : null;
    }

    public function privateDecryptBase64(string $b64): ?string {
        return $this->privateDecrypt(base64_decode($b64));
    }

    public function signBase64(string $data, string $algo = 'sha256'): ?string {
        $sig = $this->sign($data, $algo);
        return $sig ? base64_encode($sig) : null;
    }

    public function verifyBase64(string $data, string $b64Sig, string $algo = 'sha256'): int {
        return $this->verify($data, base64_decode($b64Sig), $algo);
    }

    public function __destruct() {
        if ($this->privateKeyResource) openssl_free_key($this->privateKeyResource);
        if ($this->publicKeyResource) openssl_free_key($this->publicKeyResource);
    }
}

使用範例

<?php
require 'PkiCrypto.php';

// 初始化(假設已有 private.pem、public.pem)
$crypto = new PkiCrypto('private.pem', 'public.pem');

$originalData = '敏感資料:訂單金額 10000 元,用戶 ID 12345';

// 1. 公鑰加密 → 私鑰解密(機密傳輸)
$encryptedB64 = $crypto->publicEncryptBase64($originalData);
echo "加密: $encryptedB64\n";

$decrypted = $crypto->privateDecryptBase64($encryptedB64);
echo "解密: $decrypted\n";
assert($decrypted === $originalData);

// 2. 私鑰簽名 → 公鑰驗證(完整性/來源驗證)
$signatureB64 = $crypto->signBase64($originalData, 'sha256');
echo "簽名: $signatureB64\n";

$verifyResult = $crypto->verifyBase64($originalData, $signatureB64, 'sha256');
echo "驗證: " . ($verifyResult === 1 ? '通過' : '失敗') . "\n";

// 3. 篡改測試
$tampered = $originalData . ' (被竄改)';
$verifyResult2 = $crypto->verifyBase64($tampered, $signatureB64, 'sha256');
echo "篡改驗證: " . ($verifyResult2 === 1 ? '通過' : '失敗(預期)') . "\n";

長資料加密:混合加密方案

RSA 直接加密資料長度受限(2048-bit 金鑰最大加密 245 字節)。實務上採用 混合加密:

  1. 產生隨機 AES-256 金鑰 + IV
  2. AES 加密實際資料
  3. RSA 公鑰加密 AES 金鑰
  4. 傳輸:RSA加密的AES金鑰 + IV + AES加密資料
<?php
function hybridEncrypt(string $data, $publicKeyResource): array {
    // 產生隨機 AES-256 金鑰 (32 bytes) + IV (16 bytes)
    $aesKey = random_bytes(32);
    $iv = random_bytes(16);
    
    // AES-256-CBC 加密資料
    $encryptedData = openssl_encrypt($data, 'aes-256-cbc', $aesKey, OPENSSL_RAW_DATA, $iv);
    
    // RSA 公鑰加密 AES 金鑰
    openssl_public_encrypt($aesKey, $encryptedKey, $publicKeyResource, OPENSSL_PKCS1_OAEP_PADDING);
    
    return [
        'key'   => base64_encode($encryptedKey),
        'iv'    => base64_encode($iv),
        'data'  => base64_encode($encryptedData),
    ];
}

function hybridDecrypt(array $package, $privateKeyResource): ?string {
    $encryptedKey = base64_decode($package['key']);
    $iv = base64_decode($package['iv']);
    $encryptedData = base64_decode($package['data']);
    
    // RSA 私鑰解密 AES 金鑰
    openssl_private_decrypt($encryptedKey, $aesKey, $privateKeyResource, OPENSSL_PKCS1_OAEP_PADDING);
    
    // AES 解密資料
    return openssl_decrypt($encryptedData, 'aes-256-cbc', $aesKey, OPENSSL_RAW_DATA, $iv);
}

常見填充模式對照

常數填充標準建議用途
OPENSSL_PKCS1_PADDINGPKCS#1 v1.5相容舊系統、簽章
OPENSSL_PKCS1_OAEP_PADDINGOAEP (SHA-1)加密推薦、較安全
OPENSSL_SSLV23_PADDINGSSL v23特定相容需求
OPENSSL_NO_PADDING無填充特殊用途(手動填充)

錯誤排查

  • openssl_pkey_get_private() 回傳 false → 檢查 PEM 格式、密碼、檔案權限
  • openssl_public_encrypt() 失敗 → 資料過長(改用混合加密)、金鑰不匹配
  • openssl_verify() 回傳 0 → 簽名不符、演算法不一致、公鑰非對應私鑰
  • openssl_error_string() 可取得詳細錯誤訊息
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved