Python SM2加解密

SM2是国产的非对称加密算法,对其软件层面实现支持的库有很多,比如gmsslbouncycastle,这里主要记录gmssl的Python用法。

不同的库相互之间的加解密可能存在不兼容的情况

如果需要使用对方的公钥进行加密数据,最好双方都使用同一个软件实现库,不然可能出现无法解密的情况:)
比如userA使用gmssl库和userB提供的公钥进行SM2数据加密,但是userB使用bouncycastle库和其私钥进行解密,这个时候就可能出现Invalid point encoding 115 … 的异常。

Invalid point encoding 115   at Org.BouncyCastle.Math.EC.ECCurve.DecodePoint(Byte[] encoded) in /_/crypto/src/math/ec/ECCurve.cs:line 457\n   at Org.BouncyCastle.Crypto.Engines.SM2Engine.Decrypt(Byte[] input, Int32 inOff, Int32 inLen) in /_/crypto/src/crypto/engines/SM2Engine.cs:line 119\n   at LongshineWS.Api.Utils.Sm2Utils.Decrypt(Byte[] encodeData, AsymmetricKeyParameter privateKey)\n   at LongshineWS.Api.Utils.Sm2Utils.Decrypt(String content, String privateKey)\n   at WebApi.Core.Controllers.WS.RMBasicController.BimRemoteSyncService(JObject jo)

下面主要使用两个文件main.pysecurity.py

main.py

Python
# -*- coding: utf-8 -*-
from security import SM2, generate_key_pair


# 两个模式,模式不同会影响加解密
MODE_C1C2C3 = 0
MODE_C1C3C2 = 1


if __name__ == "__main__":
    # 这里的公钥和私钥都是以16进制的形式存在的,如果对方给的公钥是base64字符或者Bytes格式,则需要先转换处理一下
    private_key, public_key = generate_key_pair()
    print("private_key is:\n{}".format(private_key))
    print("public_key is:\n{}".format(public_key))

    plaintext = "Hello World"
    print("\nplaintext is :{}".format(plaintext))
    # 如果使用对方提供的公钥进行加密,则这里参数不用传递私钥
    sm2 = SM2(public_key=public_key, private_key=private_key, mode=MODE_C1C2C3)

    ciphertext = sm2.encrypt(plaintext)
    print("ciphertext is :{}".format(ciphertext))

    plaintext = sm2.decrypt(ciphertext)
    print("plaintext is :{}".format(plaintext))
private_key is:
d20728b5e0d49e041d55fdfc2897016f5e7f68f946f3a76345958fcb08646524
public_key is:
04a107c2f6a1c3ceb9ae9f1e84b84f5fd0edf2fb952b758a4955649b886fb9b6a56fa976a561b244a7cf73b0f24aea56000acb7917de43636136e6ab6dadd49548

plaintext is :Hello World
ciphertext is :DIxIDB7ln7Lb1/A5cE26fl6w/BDXt03L6hLhTKqGNiRctj1eZN88neBf6UPrGm1Hj4E/y4BWNcPYIhUooBkZy9DncXBXJDFbSR33GPRUMjlyPe+niVYWLHAevgRSmCvU343QgLdrK5HCSSg=
plaintext is :Hello World

gmalg和gmssl可能会需要安装

pip install gmalg -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install gmssl -i https://pypi.tuna.tsinghua.edu.cn/simple

security.py

Python
# -*- coding: utf-8 -*-
from gmssl.sm2 import CryptSM2
from base64 import b64encode, b64decode
import gmalg


def generate_key_pair():
    private_key, public_key =  gmalg.sm2.SM2().generate_keypair()
    private_key = private_key.hex()
    public_key = public_key.hex()
    return private_key, public_key


class SM2:
    def __init__(self, public_key = None, private_key = None, mode=1, asn1=False):
        self.public_key = public_key
        self.private_key = private_key
        self.sm2 = None
        self.asn1 = asn1
        self.mode = mode
        pass

    def encrypt(self, plaintext):
        if self.sm2 == None:
            self.sm2 = CryptSM2(public_key=self.public_key, private_key=self.private_key, asn1=self.asn1, mode=self.mode)
        encode_plaintext = plaintext.encode(encoding="utf-8")
        ciphertext_bytes = self.sm2.encrypt(encode_plaintext)
        ciphertext_base64 = b64encode(ciphertext_bytes).decode()
        return ciphertext_base64

    def decrypt(self, ciphertext_base64):
        if self.sm2 == None:
            self.sm2 = CryptSM2(public_key=self.public_key, private_key=self.private_key, asn1=self.asn1, mode=self.mode)
        ciphertext_bytes = b64decode(ciphertext_base64.encode())
        encode_plaintext = self.sm2.decrypt(ciphertext_bytes)
        plaintext = encode_plaintext.decode(encoding="utf-8")
        return plaintext