SM2非对称算法加解密

作者:彭金龙   原文地址:http://www.jonllen.cn/jonllen/work/164.aspx

在前面文章我们已经可以制作SM2证书了,主要应用了SM2签名验证算法和SM3摘要算法,在本文中主要介绍SM2公钥加密算法。这里我们使用SM2数字证书来做SM2非对称加密,然后使用硬件加密设备做解密,比如加密文件只能由指定的智能密码钥匙UKey才能解开。

SM2加密算法

SM2加密同样使用接收方公钥加密,公钥由一个曲线坐标点组成,在X.509证书中的共钥表示为04标记开始的2个32byte的BigInteger,即曲线点P(x,y)。SM2公钥加密算法比RSA相对复杂,加密结果由3个部分组成,SM2加密过程中使用了随机数,因此同样的明文数据每一次加密结果都不一样。SM2加密算法流程如下图所示。

SM2非对称算法加解密_第1张图片

根据国密推荐的SM2椭圆曲线公钥密码算法,首先产生随机数计算出曲线点C1,2个32byte的BigInteger大数,即为SM2加密结果的第1部分。第2部分则是真正的密文,是对明文的加密结果,长度和明文一样。第3部分是杂凑值,用来效验数据。按国密推荐的256位椭圆曲线,明文加密结果比原长度会大96byte。

SM2加密算法同样也可以基于使用BouncyCastle库实现。一般使用数字证书来标识身份,同时使用证书中公钥加密数据。如下SM2Cipher类是C#下SM2软算法实现。

 
   
   
   
   
SM2Cipher.cs
1 public class SM2Cipher 2 { 3 private int ct = 1; 4 5 private ECPoint p2; 6 private SM3Digest sm3keybase; 7 private SM3Digest sm3c3; 8 9 private byte[] key = new byte[32]; 10 private byte keyOff = 0; 11 12 public SM2Cipher() { } 13 14 15 16 private void Reset() 17 { 18 sm3keybase = new SM3Digest(); 19 sm3c3 = new SM3Digest(); 20 21 byte[] p; 22 23 p = p2.X.ToBigInteger().ToByteArrayUnsigned(); 24 sm3keybase.BlockUpdate(p, 0, p.Length); 25 sm3c3.BlockUpdate(p, 0, p.Length); 26 27 p = p2.Y.ToBigInteger().ToByteArrayUnsigned(); 28 sm3keybase.BlockUpdate(p, 0, p.Length); 29 30 ct = 1; 31 NextKey(); 32 } 33 34 private void NextKey() 35 { 36 SM3Digest sm3keycur = new SM3Digest(sm3keybase); 37 sm3keycur.Update((byte)(ct >> 24 & 0x00ff)); 38 sm3keycur.Update((byte)(ct >> 16 & 0x00ff)); 39 sm3keycur.Update((byte)(ct >> 8 & 0x00ff)); 40 sm3keycur.Update((byte)(ct & 0x00ff)); 41 sm3keycur.DoFinal(key, 0); 42 keyOff = 0; 43 ct++; 44 } 45 46 public virtual ECPoint InitEncipher(ECPoint userKey) 47 { 48 BigInteger k = null; 49 ECPoint c1 = null; 50 51 if (1==1) 52 { 53 AsymmetricCipherKeyPair key = SM2CryptoServiceProvider.SM2KeyPairGenerator.GenerateKeyPair(); 54 ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private; 55 ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public; 56 57 k = ecpriv.D; 58 c1 = ecpub.Q; 59 } 60 61 p2 = userKey.Multiply(k); 62 Reset(); 63 64 return c1; 65 66 } 67 68 public virtual void Encrypt(byte[] data) 69 { 70 sm3c3.BlockUpdate(data, 0, data.Length); 71 for (int i = 0; i < data.Length; i++) 72 { 73 if (keyOff == key.Length) 74 NextKey(); 75 76 data[i] ^= key[keyOff++]; 77 } 78 } 79 80 public virtual void InitDecipher(BigInteger userD, ECPoint c1) 81 { 82 p2 = c1.Multiply(userD); 83 Reset(); 84 } 85 86 public virtual void Decrypt(byte[] data) 87 { 88 for (int i = 0; i < data.Length; i++) 89 { 90 if (keyOff == key.Length) 91 NextKey(); 92 93 data[i] ^= key[keyOff++]; 94 } 95 sm3c3.BlockUpdate(data, 0, data.Length); 96 } 97 98 public virtual void Dofinal(byte[] c3) 99 { 100 byte[] p = p2.Y.ToBigInteger().ToByteArrayUnsigned(); 101 sm3c3.BlockUpdate(p, 0, p.Length); 102 sm3c3.DoFinal(c3, 0); 103 Reset(); 104 } 105 106 107 /// 108 /// 使用SM2公钥加密数据 109 /// 110 /// 111 /// 112 /// 113 public string Encrypt(ECPoint pubKey, byte[] plaintext) 114 { 115 116 byte[] data = new byte[plaintext.Length]; 117 Array.Copy(plaintext, data, plaintext.Length); 118 119 ECPoint c1 = InitEncipher(pubKey); 120 Encrypt(data); 121 122 byte[] c3 = new byte[32]; 123 Dofinal(c3); 124 125 string hexString = c1.X.ToBigInteger().ToString(16) + c1.Y.ToBigInteger().ToString(16) 126 + Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(data) 127 + Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(c3); 128 129 return hexString; 130 131 } 132 133 /// 134 /// 使用SM2解密数据 135 /// 136 /// 137 /// 138 /// 139 public byte[] Decrypt(BigInteger privateKey, string ciphertext) 140 { 141 142 string hexString = ciphertext; 143 string c1X = hexString.Substring(0, 64); 144 string c1Y = hexString.Substring(0 + c1X.Length, 64); 145 string encrypData = hexString.Substring(c1X.Length + c1Y.Length, hexString.Length - c1X.Length - c1Y.Length - 64); 146 string c3 = hexString.Substring(hexString.Length - 64); 147 148 byte[] data = SM2CryptoServiceProvider.StrToToHexByte(encrypData); 149 150 ECPoint c1 = CreatePoint(c1X, c1Y); 151 152 InitDecipher(privateKey, c1); 153 Decrypt(data); 154 155 byte[] c3_ = new byte[32]; 156 Dofinal(c3_); 157 158 string decryptData = Encoding.Default.GetString(data); 159 bool isDecrypt = Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(c3_) == c3; 160 161 return (isDecrypt ? data : new byte[0]); 162 } 163 164 165 /// 166 /// 创建坐标点 167 /// 168 /// 169 /// 170 /// 171 public static ECPoint CreatePoint(string x, string y) 172 { 173 174 BigInteger biX = new BigInteger(x, 16); 175 BigInteger biY = new BigInteger(y, 16); 176 ECFieldElement fx = new FpFieldElement(SM2CryptoServiceProvider.ecc_p, biX); 177 ECFieldElement fy = new FpFieldElement(SM2CryptoServiceProvider.ecc_p, biY); 178 ECPoint point = new FpPoint(SM2CryptoServiceProvider.ecc_curve, fx,fy); 179 return point; 180 } 181 182 183 184}

SM2解密算法

SM2解密算法是加密逆运算。首先需要从密文中取出加密结果的3部分值,然后通过私钥计算出 M'明文值,最后效验数据。SM2解密算法流程如下图所示。 

SM2非对称算法加解密_第2张图片

 SM2解密同样也可以使用软算法实现。但因为涉及到私钥运算,为保护私钥安全,建议在硬件设备中运行,例如UKey等存储介质这样可以更好的保护密钥安全。拿文件加密来说,首先拿UKey里面的加密证书加密,这部分可在应用系统内完成。解密的话则需要加密证书对应UKey才能做解密,由应用系统调用UKey解密接口,在物理硬件内完成数据解密,同时可以受设备Ping码保护。

附:SM2算法加解密在线测试工具

你可能感兴趣的:(Win32,编程开发,SM2)