前言:
RSA下加密,解密,加簽和驗簽是四種不同的操作,有時候會搞錯,記錄一下。
1.公鑰加密,私鑰解密
發送方通過公鑰將原數據加密成一個sign參數,相當于就是信息的載體,接收方能通過sign解密出原數據
/*** 公鑰加密*/public static function encrypt($string,$type,$params=[]):string{try{$params['act'] = $params['act']??[];$publicKey = file_get_contents( app()->getRootPath() . "storage/resource/{$type}/public_key.pem");openssl_public_encrypt($string, $encrypted, $publicKey);$encrypted = base64_encode($encrypted);if(in_array('rawurlencode',$params['act'])){$encrypted = rawurlencode($encrypted);}return $encrypted;} catch (\Exception $e) {throw new \Exception($e->getMessage());}/*** 私鑰解密*/public static function decrypt($string,$type,$params=[]):string{try{$params['act'] = $params['act']??[];$privateKeyContent = file_get_contents( app()->getRootPath() . "storage/resource/{$type}/private_key.pem");$privateKey = openssl_pkey_get_private([$privateKeyContent,$params['password'],]);if ($privateKey === false) {throw new \Exception('私鑰加載失敗:' . openssl_error_string());}// 4. 解密數據$string = base64_decode($string);if(in_array('rawurldecode',$params['act'])){$string = rawurldecode($string);}$decryptSuccess = openssl_private_decrypt($string, $decryptedData, $privateKey);if (!$decryptSuccess) {throw new \Exception('解密失敗:' . openssl_error_string());}//釋放內存openssl_free_key($privateKey);return $decryptedData;} catch (\Exception $e) {throw new \Exception($e->getMessage());}}
使用
1.第三方向本站發送數據,先將公鑰同步給第三方。
2.第三方將 name=xiaozhao&age=20 用公鑰直接加密成sign參數。
3.第三方將sign發送給我站。
4.我站用私鑰將xxx重新解密成 name=xiaozhao&age=20。
4.原數據加密為sign狀態下傳輸。
2.私鑰加簽,公鑰驗簽
我方和接收方事先協商好加簽規則,然后將原數據生成一個sign參數,接收方將一起帶來的原數據跟sign參數對比,看是否一致。
/*** 私鑰加簽*/public static function sign($string, $type,$params=[]){try {$privateKeyContent = file_get_contents( app()->getRootPath() . "storage/resource/{$type}/private_key.pem");$privateKey = openssl_pkey_get_private([$privateKeyContent,$params['password'],]);if ($privateKey === false) {throw new \Exception('私鑰加載失敗:' . openssl_error_string());}// 4. 創建簽名$signature = '';if (!openssl_sign($string, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {throw new \Exception('簽名創建失敗: ' . openssl_error_string());}// 5. 返回Base64編碼的簽名return base64_encode($signature);} catch (\Exception $e) {throw new \Exception("簽名過程中發生錯誤: " . $e->getMessage());}}/*** 公鑰驗簽*/public static function verify($string, $type, $sign) {try {$publicKeyContent = file_get_contents(app()->getRootPath() . "storage/resource/{$type}/public_key.pem");// 關鍵修復:解析公鑰為 OpenSSL 資源$publicKey = openssl_pkey_get_public($publicKeyContent);if ($publicKey === false) {throw new \Exception('公鑰加載失敗: ' . openssl_error_string());}// 清理簽名數據$sign = base64_decode(trim(str_replace(["\r", "\n", " "], '', $sign)));$result = openssl_verify($string, $sign, $publicKey, OPENSSL_ALGO_SHA256);// 釋放密鑰資源openssl_free_key($publicKey);if ($result === -1) {throw new \Exception('參數無效或密鑰格式錯誤: ' . openssl_error_string());}if ($result === 0) {throw new \Exception('驗證失敗');}return $result === 1;} catch (\Exception $e) {throw new \Exception("驗簽失敗: " . $e->getMessage());}}
使用
密鑰 MIGfMA0G 開頭的是PKCS#1 格式,MIIBIjANBgkqh 開頭的是PKCS#8 格式。PKCS#8格式提取公鑰的時候要先用openssl_pkey_get_public提取
1.本站向第三方發送數據,先將公鑰發給第三方。
2.我站將 name=xiaozhao&age=20用雙方協商好的方式排序(如去掉空數據,再按鍵名排序等)后,用私鑰生成簽名sign。
3.我站將原數據和sign一起
發送給我站。
3.第三方將原參數用雙方協商好的方式排序后,和sign參數用公鑰驗簽
4.原數據和sign同時傳輸傳輸。