sm4解密乱码或空字符串主因是填充不一致:php类默认pkcs#7填充,而gmssl命令行默认不填充且要求明文16字节对齐,需两端严格统一填充方式,并确保cbc模式下iv相同且正确传递。

SM4 适合对称加密场景,SM2 用于非对称密钥交换或签名验签——两者不能混用,选错直接导致解密失败或验签不过。
为什么 SM4 解密总是返回乱码或空字符串?
常见原因是填充(padding)不一致。PHP纯实现中,绝大多数Sm4Helper类默认使用PKCS#7填充,但如果你用GmSSL命令行加密(如gmssl sm4 -encrypt),它默认不填充,且输入必须是16字节对齐的原始数据。
- 确保加解密两端填充方式完全一致:要么都开启PKCS#7,要么都禁用(需手动补零)
- 明文长度不是16倍数时,纯PHP类会自动填充;而
GmSSL要求你提前处理:str_pad($plaintext, (ceil(strlen($plaintext) / 16) * 16), "\0", STR_PAD_RIGHT)
- ECB模式下尤其明显;CBC模式还需校验
iv是否相同且传递正确
SM2 加密后前端/银行系统无法解密?
核心问题往往出在公钥格式和密文结构上。招商银行、银联等机构要求的SM2密文是“C1C3C2”拼接格式(即椭圆曲线点+杂凑值+密文),但很多PHP库(包括部分lpilp/guomi旧版)默认输出的是“C1C2C3”。
- 检查你调用的
doEncrypt()方法返回值顺序,必要时手动重组:$ciphertext = $c1 . $c3 . $c2 - 公钥传入前必须去除PEM头尾,并确认是否已转为无格式的
04 + x + y格式(65字节);若银行提供的是PKCS#8 Base64串,要用MyAsn1::decode()或openssl_pkey_get_public()提取原始坐标 - 注意:部分银行接口要求密文Base64编码后再传输,别漏掉这步
SM4 该用 ECB 还是 CBC?生产环境怎么选?
ECB模式简单,但绝对不能用于生产:相同明文块永远生成相同密文块,存在严重模式泄露风险(比如加密固定格式的身份证号,攻击者可轻易识别字段位置)。
- 生产必须用
CBC或CTR模式,且iv必须随机生成、每次不同,并随密文一同传输(通常前置16字节) -
lpilp/guomi的sm4_encrypt()默认是ECB;要切CBC,得显式传参:$cipher = new Sm4(); $cipher->setMode(Sm4::MODE_CBC); $cipher->setIv($iv);
- 若用
GmSSL命令行,CBC需显式指定-iv参数,且iv必须是16字节十六进制字符串(如000102030405060708090a0b0c0d0e0f)
密钥管理比算法本身更易出问题:128位SM4密钥必须严格为16字节二进制数据,传入字符串时别忘了hex2bin()或pack('H*', $keyHex);SM2私钥若从文件读取,确保没带换行或空格——这些细节一旦错,错误信息往往只报“decryption failed”,实际跟算法无关。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











