php sm4踩坑记录

由于工作需要,需要实现多个算法对sm4的实现,并且可以互相加密解密。本来想着开源算法网上又有很多库应该问题不大,但是却走了很多弯路,记录下来以免以后再犯。

本文只记录了php和python的方法,其他方法点应该都差不多。先要感谢https://github.com/lizhichao/sm。已经实现了算法,只是有以下几个问题:

1、padding的方式是用空格的,这种跟其他语言不一样,其他语言基本都是用PKCS5Padding或者PKCS7Padding来实现的,这样就导致计算出来的内容不一致,需要修改为PKCS5Padding的话,需要修改dd函数。

private function dd(&$data)
    {
        $n    = strlen($data) % $this->len;
        $data = $data . str_repeat($this->b, $n);
    }
修改为:
private function dd(&$data)
    {
        $n    = $this->len - (strlen($data) % $this->len);
        $data = $data . str_repeat(chr($n), $n);
    }

2、在调用解密函数的时候没有进行删除padding操作,导致解密出来的内容与原文不一致。需要添加udd函数,并在deDataEcb时进行调用。

private function udd(&$data)
{
        $n = ord($data[strlen($data) - 1]);
        $data = substr($data, 0, -1 * $n);
        return $data;
}

public function deDataEcb($str)
    {
        $r = [];
-        $this->dd($str);
        $ar = unpack('N*', $str);
        do {
            $this->decode([current($ar), next($ar), next($ar), next($ar)], $r);
        } while (next($ar));
-       return pack('N*', ...$r);
+       $rs = pack('N*', ...$r);
+       return $this->udd($rs);
    }

3、加密后的内容为二进制无法直接查看,如果要直观查看的话可以调用hex2bin函数转换为十六进制来进行查看。

python调用sm4很简单,直接使用pip安装sm4库就可以,但是padding是默认false的,如果实际使用还是要打开

# -*- coding: utf-8 -*-

from hashlib import md5
from sm4 import SM4Key

appId = 'test'
data = 'sm4 testing encoding'
secretkey = md5(bytes(appId, encoding="utf8")).digest()
key = SM4Key(secretkey)
sm = key.encrypt(bytes(data, encoding="utf8"), padding=True)
sign = sm.hex()