前言
國密主要有SM1,SM2,SM3,SM4。金鑰長度和分組長度均為128位。
SM1
為對稱加密。其加密強度與AES(高階加密標準,Advanced Encryption Standard)相當。該演算法不公開,呼叫該演算法時,需要通過加密晶片的介面進行呼叫。SM2
為非對稱加密,基於ECC。該演算法已公開。由於該演算法基於ECC,故其簽名速度與祕鑰生成速度都快於RSA。ECC 256位(SM2採用的就是ECC 256位的一種)安全強度比RSA 2048位高,但運算速度快於RSA。SM3
為訊息摘要。可以用MD5作為對比理解。該演算法已公開。校驗結果為256位。SM4
為對稱加密,無線區域網標準的分組資料演算法,金鑰長度和分組長度均為128位。
程式碼
安裝包
composer require lpilp/guomi
示例
// 工具函式 function formatHex($dec) { $hex = gmp_strval(gmp_init($dec, 10), 16); $len = strlen($hex); if ($len == 64) { return $hex; } if ($len < 64) { $hex = str_pad($hex, 64, "0", STR_PAD_LEFT); } else { $hex = substr($hex, $len - 64, 64); } return $hex; } ############################資料加密開始################################ // 公鑰 $publicKey = 'BNsIe9U0x8IeSe4h/dxUzVEz9pie0hDSfMRINRXc7s1UIXfkExnYECF4QqJ2SnHxLv3z/99gsfDQrQ6dzN5lZj0='; // 私鑰 $privateKey = 'NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E='; // base64私鑰轉二進位制 $privateKey = base64_decode($privateKey); // 二進位制轉十六進位制字串 $privateKey = unpack("H*", $privateKey)[1]; // 待加密的資料 $data = '{"request":{"body":{"ntbusmody":[{"busmod":"00001"}],"ntdumaddx1":[{"bbknbr":"75","dyanam":"招商測試","dyanbr":"11111111111","eftdat":"20220602","inbacc":"755936020410404","ovrctl":"N","yurref":"596620626253316098"}]},"head":{"funcode":"NTDUMADD","reqid":"202206021511010000001","userid":"B000001631"}},"signature":{"sigdat":"__signature_sigdat__","sigtim":"20220602161503"}}'; // 生成簽名開始 $sm2 = new RtSm2("base64"); // 將使用者id填充到16個位元組 $userId = sprintf('%-016s', "B000001631"); // 使用rsa的私鑰生成簽名(注意這裡是私鑰!私鑰!私鑰!) $sign = $sm2->doSign($data, $privateKey, $userId); // 將base64的簽名還原為二進位制 $sign = base64_decode($sign); // 處理二進位制資料 $point = \FG\ASN1\ASNObject::fromBinary($sign)->getChildren(); $pointX = formatHex($point[0]->getContent()); $pointY = formatHex($point[1]->getContent()); $sign = $pointX . $pointY; $sign = base64_encode(hex2bin($sign)); // 替換籤名欄位 $data = str_replace('__signature_sigdat__', $sign, $data); // 對資料進行對稱加密(換成你自己的key) $sm4 = new RtSm4('VuAzSWQhsoNqzn0K'); // 這裡使用的具名引數的寫法,低版本的php改成順序傳入引數就行 $encryptData = $sm4->encrypt($data, 'sm4-cbc', $iv = $userId, "base64"); var_dump($encryptData);die; ############################資料加密結束################################ ############################返回資料驗證開始################################ $decryptData = "LkQOOa0kJr7xWxyhr1kj4mf31f1lZOv5bURemjcALkmQXGeKBIVnR6f+BIN8g6UvhHy08LKrmyYTq9LBXQBI95i7Ht/4OWTRFoFG/lCYT39cr50a426UgreuF4NUrUdCGoItHiwTmCcfJStqjdGXY0O0lr9YR2GJZEOtpllnRThoIWEIdPUvQMtUyzfQKuOZ6s7r6V3jirKUFuaeuFtuZ96RliOCqQa/BdCY/qHnjVaMEoZNTYeHeUIcZs43nCxaMcvaBFTZ9wbBjNf3jwmi/TZKHIcXLQpIxtWdYoOC12dgKkeBL83xaHCGYpvkOO0IFML8XbJR1oQJdvvF49WCN6HmrcikG0fPjX+AzTxT1odHsAwHk78m9galKfkslUDrT+bq4qplw3ByOQA+5WfzmNPsSgGYLfE6va+5EbXieaMW6pPs7yiWUyOhpVOpBV+6q4cwXWeGgDgUhXQ1dTKFqqJQBMKX8iRvXgYFTmwSzZHvH7VZmtuf7gZMMtycSUFb"; // 返回結果解密,這裡使用的具名引數的寫法,低版本的php改成順序傳入引數就行 $json = $sm4->decrypt($decryptData, $type = 'sm4-cbc', $iv = $userId, $formatInput = 'base64'); $data = json_decode($json, true); var_dump($data);die; // 驗證簽名是否正確 $sign = $data["signature"]["sigdat"]; // 將資料中的簽名重置 $data["signature"]["sigdat"] = "__signature_sigdat__"; $json = json_encode($data, 256); $signHex = bin2hex(base64_decode($sign)); $r = substr($signHex, 0, 64); $s = substr($signHex, 64, 64); $r = gmp_init($r, 16); $s = gmp_init($s, 16); $signature = new \Mdanter\Ecc\Crypto\Signature\Signature($r, $s); $serializer = new DerSignatureSerializer(); $serializedSig = $serializer->serialize($signature); $sign = base64_encode($serializedSig); $publicKey = unpack("H*", base64_decode($publicKey))[1]; $b = $sm2->verifySign($json, $sign, $publicKey, $userId); var_dump($b); ############################返回資料驗證結束################################
本作品採用《CC 協議》,轉載必須註明作者和本文連結