金流、API 對接、電子合約常需數位簽章確保資料完整性、不可否認性。PHP openssl_* 函數提供完整 PKI 操作。本文實戰演示:載入 PKCS#12 (.p12/.pfx) 私鑰簽名、載入 PEM 公鑰驗證、支援 SHA1/SHA256/SM3 等演算法。
openssl_sign() → 輸出二進位簽名。openssl_verify() → 回傳 1=成功/0=失敗/-1=錯誤。<?php
class DigitalSigner {
private $p12Path;
private $p12Pass;
private $pemPath;
public function __construct(string $p12Path, string $p12Pass, string $pemPath = '') {
$this->p12Path = $p12Path;
$this->p12Pass = $p12Pass;
$this->pemPath = $pemPath;
}
// 簽名(預設 SHA1,可指定 SHA256 等)
public function sign(string $data, int $algo = OPENSSL_ALGO_SHA1): string {
$pkcs12 = file_get_contents($this->p12Path);
if (!openssl_pkcs12_read($pkcs12, $certs, $this->p12Pass)) {
throw new RuntimeException('PKCS#12 讀取失敗,請檢查密碼');
}
$privateKey = $certs['pkey'] ?? throw new RuntimeException('私鑰不存在');
$signature = '';
if (!openssl_sign($data, $signature, $privateKey, $algo)) {
throw new RuntimeException('簽名失敗: ' . openssl_error_string());
}
return $signature; // 二進位原始簽名
}
// 驗證(用同一 .p12 的公鑰自驗)
public function verifySelf(string $data, string $signature, int $algo = OPENSSL_ALGO_SHA1): bool {
$pkcs12 = file_get_contents($this->p12Path);
if (!openssl_pkcs12_read($pkcs12, $certs, $this->p12Pass)) return false;
$publicKey = $certs['cert'] ?? throw new RuntimeException('公鑰不存在');
$ok = openssl_verify($data, $signature, $publicKey, $algo);
return $ok === 1;
}
// 驗證(用外部 PEM 公鑰,如對方給的驗證憑證)
public function verifyWithPem(string $data, string $signature, int $algo = OPENSSL_ALGO_SHA1): bool {
if (!$this->pemPath || !file_exists($this->pemPath)) return false;
$pem = file_get_contents($this->pemPath);
$pubKeyId = openssl_get_publickey($pem);
if (!$pubKeyId) return false;
$ok = openssl_verify($data, $signature, $pubKeyId, $algo);
openssl_free_key($pubKeyId);
return $ok === 1;
}
}
?>
<?php
$signer = new DigitalSigner('merchant.p12', 'p12_password', 'bank_public.pem');
$data = 'order_id=12345&amount=100.00×tamp=1699999999';
// 簽名(建議用 SHA256 以上)
$signature = $signer->sign($data, OPENSSL_ALGO_SHA256);
// 傳輸時通常 Base64 編碼
$sigB64 = base64_encode($signature);
// 對方驗證
if ($signer->verifyWithPem($data, $signature, OPENSSL_ALGO_SHA256)) {
echo '驗證通過';
}
?>
| 常數 | 演算法 | 備註 |
|---|---|---|
| OPENSSL_ALGO_SHA1 | SHA-1 | 預設,已不建議新系統使用 |
| OPENSSL_ALGO_SHA224 | SHA-224 | |
| OPENSSL_ALGO_SHA256 | SHA-256 | ✅ 推薦最低標準 |
| OPENSSL_ALGO_SHA384 | SHA-384 | |
| OPENSSL_ALGO_SHA512 | SHA-512 | |
| OPENSSL_ALGO_RMD160 | RIPEMD-160 | |
| OPENSSL_ALGO_MD5 | MD5 | ❌ 不安全,禁用 |
| OPENSSL_ALGO_DSS1 | DSA-SHA1 | DSA 金鑰專用 |
💡 國密 SM2/SM3 需 PHP 編譯啟用 --enable-openssl 且 OpenSSL 库支援 GM/T 标准,調用方式相同,常數可能為 OPENSSL_ALGO_SM3(視版本)。
openssl_pkcs12_read 返回 false,無錯誤訊息,需自行檢查。$certs['pkey']、$certs['cert'],不同 OpenSSL 版本可能不同,建議 print_r($certs) 確認。base64_encode,驗證前 base64_decode。openssl_x509_verify() 驗證憑證鏈信任鍊,防止偽造憑證。PHP OpenSSL 簽名驗證核心就三個函數:openssl_pkcs12_read、openssl_sign、openssl_verify。封裝好工具類、統一演算法(SHA256+)、規範化資料格式,即可滿足金流、開放平台 99% 的簽名需求。