民生银行的库DLL只有C版本和JAVA版本。按着JAVA版本做的C# 实现。

重点内容。

1。数字信封就是 CmsEnvelopedData Der编码后转BASE64

2。重点类:ContentInfo、EnvelopedData、EncryptedContentInfo、RecipientInfo。

3。随机生成 SM4 key和IV,都是16位。其中:SM4 key 使用SM2加密后,放入RecipientInfo。

4。IV 使用 DerOctetString 编码后,使用 AlgorithmIdentifier 携带。

5。把待加密数据用SM4 CBC 加密,Ber编码后放入 EncryptedContentInfo。

6。CmsEnvelopedData 包含 ContentInfo、EnvelopedData、EncryptedContentInfo、RecipientInfo。

7。SM2加密后,密文会是 04 开头,要去掉后传给JAVA(民生)。

实现:

nuget 下载 BouncyCastle,1.8.9 版本。

GmUtil:

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;namespace CommonUtils
{/*** need lib:* BouncyCastle.Crypto.dll(http://www.bouncycastle.org/csharp/index.html) * 用BC的注意点:* 这个版本的BC对SM3withSM2的结果为asn1格式的r和s,如果需要直接拼接的r||s需要自己转换。下面rsAsn1ToPlainByteArray、rsPlainByteArrayToAsn1就在干这事。* 这个版本的BC对SM2的结果为C1||C2||C3,据说为旧标准,新标准为C1||C3||C2,用新标准的需要自己转换。下面(被注释掉的)changeC1C2C3ToC1C3C2、changeC1C3C2ToC1C2C3就在干这事。java版的高版本有加上C1C3C2,csharp版没准以后也会加,但目前还没有,java版的目前可以初始化时“ SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);”。* * 按要求国密算法仅允许使用加密机,本demo国密算法仅供学习使用,请不要用于生产用途。*/public class GmUtil{//private static readonly ILog log = LogManager.GetLogger(typeof(GmUtil));private static X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1");private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);/**** @param msg* @param userId* @param privateKey* @return r||s,直接拼接byte数组的rs*/public static byte[] SignSm3WithSm2(byte[] msg, byte[] userId, AsymmetricKeyParameter privateKey){return RsAsn1ToPlainByteArray(SignSm3WithSm2Asn1Rs(msg, userId, privateKey));}/*** @param msg* @param userId* @param privateKey* @return rs in <b>asn1 format</b>*/public static byte[] SignSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, AsymmetricKeyParameter privateKey){try{ISigner signer = SignerUtilities.GetSigner("SM3withSM2");signer.Init(true, new ParametersWithID(privateKey, userId));signer.BlockUpdate(msg, 0, msg.Length);byte[] sig = signer.GenerateSignature();return sig;}catch (Exception e){//log.Error("SignSm3WithSm2Asn1Rs error: " + e.Message, e);return null;}}/**** @param msg* @param userId* @param rs r||s,直接拼接byte数组的rs* @param publicKey* @return*/public static bool VerifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, AsymmetricKeyParameter publicKey){if (rs == null || msg == null || userId == null) return false;if (rs.Length != RS_LEN * 2) return false;return VerifySm3WithSm2Asn1Rs(msg, userId, RsPlainByteArrayToAsn1(rs), publicKey);}/**** @param msg* @param userId* @param rs in <b>asn1 format</b>* @param publicKey* @return*/public static bool VerifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] sign, AsymmetricKeyParameter publicKey){try{ISigner signer = SignerUtilities.GetSigner("SM3withSM2");signer.Init(false, new ParametersWithID(publicKey, userId));signer.BlockUpdate(msg, 0, msg.Length);return signer.VerifySignature(sign);}catch (Exception e){//log.Error("VerifySm3WithSm2Asn1Rs error: " + e.Message, e);return false;}}/*** bc加解密使用旧标c1||c2||c3,此方法在加密后调用,将结果转化为c1||c3||c2* @param c1c2c3* @return*/private static byte[] ChangeC1C2C3ToC1C3C2(byte[] c1c2c3){int c1Len = (x9ECParameters.Curve.FieldSize + 7) / 8 * 2 + 1; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。const int c3Len = 32; //new SM3Digest().getDigestSize();byte[] result = new byte[c1c2c3.Length];Buffer.BlockCopy(c1c2c3, 0, result, 0, c1Len); //c1Buffer.BlockCopy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3Buffer.BlockCopy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2return result;}/*** bc加解密使用旧标c1||c3||c2,此方法在解密前调用,将密文转化为c1||c2||c3再去解密* @param c1c3c2* @return*/private static byte[] ChangeC1C3C2ToC1C2C3(byte[] c1c3c2){int c1Len = (x9ECParameters.Curve.FieldSize + 7) / 8 * 2 + 1; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。const int c3Len = 32; //new SM3Digest().GetDigestSize();byte[] result = new byte[c1c3c2.Length];Buffer.BlockCopy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65Buffer.BlockCopy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2Buffer.BlockCopy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3return result;}/*** c1||c3||c2* @param data* @param key* @return*/public static byte[] Sm2Decrypt(byte[] data, AsymmetricKeyParameter key){return Sm2DecryptOld(ChangeC1C3C2ToC1C2C3(data), key);}/*** c1||c3||c2* @param data* @param key* @return*/public static byte[] Sm2Encrypt(byte[] data, AsymmetricKeyParameter key){return ChangeC1C2C3ToC1C3C2(Sm2EncryptOld(data, key));}/*** c1||c2||c3* @param data* @param key* @return*/public static byte[] Sm2EncryptOld(byte[] data, AsymmetricKeyParameter pubkey){try{SM2Engine sm2Engine = new SM2Engine();sm2Engine.Init(true, new ParametersWithRandom(pubkey, new SecureRandom()));return sm2Engine.ProcessBlock(data, 0, data.Length);}catch (Exception e){//log.Error("Sm2EncryptOld error: " + e.Message, e);return null;}}/*** c1||c2||c3* @param data* @param key* @return*/public static byte[] Sm2DecryptOld(byte[] data, AsymmetricKeyParameter key){try{SM2Engine sm2Engine = new SM2Engine();sm2Engine.Init(false, key);return sm2Engine.ProcessBlock(data, 0, data.Length);}catch (Exception e){//log.Error("Sm2DecryptOld error: " + e.Message, e);return null;}}/*** @param bytes* @return*/public static byte[] Sm3(byte[] bytes){try{SM3Digest digest = new SM3Digest();digest.BlockUpdate(bytes, 0, bytes.Length);byte[] result = DigestUtilities.DoFinal(digest);return result;}catch (Exception e){//log.Error("Sm3 error: " + e.Message, e);return null;}}private const int RS_LEN = 32;private static byte[] BigIntToFixexLengthBytes(BigInteger rOrS){// for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123,// r and s are the result of mod n, so they should be less than n and have length<=32byte[] rs = rOrS.ToByteArray();if (rs.Length == RS_LEN) return rs;else if (rs.Length == RS_LEN + 1 && rs[0] == 0) return Arrays.CopyOfRange(rs, 1, RS_LEN + 1);else if (rs.Length < RS_LEN){byte[] result = new byte[RS_LEN];Arrays.Fill(result, (byte)0);Buffer.BlockCopy(rs, 0, result, RS_LEN - rs.Length, rs.Length);return result;}else{throw new ArgumentException("err rs: " + Hex.ToHexString(rs));}}/*** BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s* @param rsDer rs in asn1 format* @return sign result in plain byte array*/private static byte[] RsAsn1ToPlainByteArray(byte[] rsDer){Asn1Sequence seq = Asn1Sequence.GetInstance(rsDer);byte[] r = BigIntToFixexLengthBytes(DerInteger.GetInstance(seq[0]).Value);byte[] s = BigIntToFixexLengthBytes(DerInteger.GetInstance(seq[1]).Value);byte[] result = new byte[RS_LEN * 2];Buffer.BlockCopy(r, 0, result, 0, r.Length);Buffer.BlockCopy(s, 0, result, RS_LEN, s.Length);return result;}/*** BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式* @param sign in plain byte array* @return rs result in asn1 format*/private static byte[] RsPlainByteArrayToAsn1(byte[] sign){if (sign.Length != RS_LEN * 2) throw new ArgumentException("err rs. ");BigInteger r = new BigInteger(1, Arrays.CopyOfRange(sign, 0, RS_LEN));BigInteger s = new BigInteger(1, Arrays.CopyOfRange(sign, RS_LEN, RS_LEN * 2));Asn1EncodableVector v = new Asn1EncodableVector();v.Add(new DerInteger(r));v.Add(new DerInteger(s));try{return new DerSequence(v).GetEncoded("DER");}catch (IOException e){//log.Error("RsPlainByteArrayToAsn1 error: " + e.Message, e);return null;}}public static AsymmetricCipherKeyPair GenerateKeyPair(){try{ECKeyPairGenerator kpGen = new ECKeyPairGenerator();kpGen.Init(new ECKeyGenerationParameters(ecDomainParameters, new SecureRandom()));return kpGen.GenerateKeyPair();}catch (Exception e){//log.Error("generateKeyPair error: " + e.Message, e);return null;}}public static ECPrivateKeyParameters GetPrivatekeyFromD(BigInteger d){return new ECPrivateKeyParameters(d, ecDomainParameters);}public static ECPublicKeyParameters GetPublickeyFromXY(BigInteger x, BigInteger y){return new ECPublicKeyParameters(x9ECParameters.Curve.CreatePoint(x, y), ecDomainParameters);}public static AsymmetricKeyParameter GetPublickeyFromX509File(FileInfo file){FileStream fileStream = null;try{//file.DirectoryName + "\\" + file.NamefileStream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read);X509Certificate certificate = new X509CertificateParser().ReadCertificate(fileStream);return certificate.GetPublicKey();}catch (Exception e){//log.Error(file.Name + "读取失败,异常:" + e);}finally{if (fileStream != null)fileStream.Close();}return null;}public static AsymmetricKeyParameter GetPublickeyFromX509String(string publicKeyCert){//还原完整文本格式publicKeyCert = StrToPubCert(publicKeyCert);byte[] bytesCerContent = System.Text.Encoding.UTF8.GetBytes(publicKeyCert);//需要完整文本格式才能解析X509Certificate certificate = new X509CertificateParser().ReadCertificate(bytesCerContent);return certificate.GetPublicKey();}/// <summary>/// 转为完整格式/// </summary>/// <param name="pubKeyStr"></param>/// <returns></returns>public static string StrToPubCert(string publicKeyCert){//去除意外字符             publicKeyCert = publicKeyCert.Replace("-----BEGIN CERTIFICATE-----", "").Replace("-----END CERTIFICATE-----", "").Replace("\r", "").Replace("\n", "").Trim();//完整string rn = "\n";StringBuilder sbKey = new StringBuilder(publicKeyCert);int nKeyLen = sbKey.Length;      //sbKey为base64编码的公钥字符串  for (int i = 64; i < nKeyLen; i += 64){sbKey.Insert(i, rn);i++;}sbKey.Insert(0, "-----BEGIN CERTIFICATE-----" + rn);sbKey.Append(rn + "-----END CERTIFICATE-----" + rn);string all_key_str = sbKey.ToString();return all_key_str;}public class Sm2Cert{public AsymmetricKeyParameter privateKey;public AsymmetricKeyParameter publicKey;public String certId;}private static byte[] ToByteArray(int i){byte[] byteArray = new byte[4];byteArray[0] = (byte)(i >> 24);byteArray[1] = (byte)((i & 0xFFFFFF) >> 16);byteArray[2] = (byte)((i & 0xFFFF) >> 8);byteArray[3] = (byte)(i & 0xFF);return byteArray;}/*** 字节数组拼接** @param params* @return*/private static byte[] Join(params byte[][] byteArrays){List<byte> byteSource = new List<byte>();for (int i = 0; i < byteArrays.Length; i++){byteSource.AddRange(byteArrays[i]);}byte[] data = byteSource.ToArray();return data;}/*** 密钥派生函数** @param Z* @param klen*            生成klen字节数长度的密钥* @return*/private static byte[] KDF(byte[] Z, int klen){int ct = 1;int end = (int)Math.Ceiling(klen * 1.0 / 32);List<byte> byteSource = new List<byte>();try{for (int i = 1; i < end; i++){byteSource.AddRange(GmUtil.Sm3(Join(Z, ToByteArray(ct))));ct++;}byte[] last = GmUtil.Sm3(Join(Z, ToByteArray(ct)));if (klen % 32 == 0){byteSource.AddRange(last);}elsebyteSource.AddRange(Arrays.CopyOfRange(last, 0, klen % 32));return byteSource.ToArray();}catch (Exception e){//log.Error("KDF error: " + e.Message, e);}return null;}public static byte[] Sm4DecryptCBC(byte[] keyBytes, byte[] cipher, byte[] iv, String algo){if (keyBytes.Length != 16) throw new ArgumentException("err key length");//如果是 NoPadding, SM4 加密的数据(byte[] plain)得是16的倍数,且需要自己Paddingif (algo.Contains("NOPAD") && cipher.Length % 16 != 0) throw new ArgumentException("err data length");try{KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);IBufferedCipher c = CipherUtilities.GetCipher(algo);if (iv == null) iv = ZeroIv(algo);c.Init(false, new ParametersWithIV(key, iv));return c.DoFinal(cipher);}catch (Exception e){//log.Error("Sm4DecryptCBC error: " + e.Message, e);return null;}}public static byte[] Sm4EncryptCBC(byte[] keyBytes, byte[] plain, byte[] iv, String algo){if (keyBytes.Length != 16) throw new ArgumentException("err key length");//如果是 NoPadding, SM4 加密的数据(byte[] plain)得是16的倍数,且需要自己Paddingif (algo.Contains("NOPAD") && plain.Length % 16 != 0) throw new ArgumentException("err data length");try{KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);IBufferedCipher c = CipherUtilities.GetCipher(algo);if (iv == null) iv = ZeroIv(algo);c.Init(true, new ParametersWithIV(key, iv));return c.DoFinal(plain);}catch (Exception e){//log.Error("Sm4EncryptCBC error: " + e.Message, e);return null;}}public static byte[] Sm4EncryptECB(byte[] keyBytes, byte[] plain, string algo){if (keyBytes.Length != 16) throw new ArgumentException("err key length");if (plain.Length % 16 != 0) throw new ArgumentException("err data length");try{KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);IBufferedCipher c = CipherUtilities.GetCipher(algo);c.Init(true, key);return c.DoFinal(plain);}catch (Exception e){//log.Error("Sm4EncryptECB error: " + e.Message, e);return null;}}public static byte[] Sm4DecryptECB(byte[] keyBytes, byte[] cipher, string algo){if (keyBytes.Length != 16) throw new ArgumentException("err key length");if (cipher.Length % 16 != 0) throw new ArgumentException("err data length");try{KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);IBufferedCipher c = CipherUtilities.GetCipher(algo);c.Init(false, key);return c.DoFinal(cipher);}catch (Exception e){//log.Error("Sm4DecryptECB error: " + e.Message, e);return null;}}public const String SM4_ECB_NOPADDING = "SM4/ECB/NoPadding";public const String SM4_CBC_NOPADDING = "SM4/CBC/NoPadding";public const String SM4_CBC_PKCS7PADDING = "SM4/CBC/PKCS7Padding";/*** cfca官网CSP沙箱导出的sm2文件* @param pem 二进制原文* @param pwd 密码* @return*/public static Sm2Cert readSm2File(byte[] pem, String pwd){Sm2Cert sm2Cert = new Sm2Cert();try{Asn1Sequence asn1Sequence = (Asn1Sequence)Asn1Object.FromByteArray(pem);//            ASN1Integer asn1Integer = (ASN1Integer) asn1Sequence.getObjectAt(0); //version=1Asn1Sequence priSeq = (Asn1Sequence)asn1Sequence[1];//private keyAsn1Sequence pubSeq = (Asn1Sequence)asn1Sequence[2];//public key and x509 cert//            ASN1ObjectIdentifier sm2DataOid = (ASN1ObjectIdentifier) priSeq.getObjectAt(0);//            ASN1ObjectIdentifier sm4AlgOid = (ASN1ObjectIdentifier) priSeq.getObjectAt(1);Asn1OctetString priKeyAsn1 = (Asn1OctetString)priSeq[2];byte[] key = KDF(System.Text.Encoding.UTF8.GetBytes(pwd), 32);byte[] priKeyD = Sm4DecryptCBC(Arrays.CopyOfRange(key, 16, 32),priKeyAsn1.GetOctets(),Arrays.CopyOfRange(key, 0, 16), SM4_CBC_PKCS7PADDING);sm2Cert.privateKey = GetPrivatekeyFromD(new BigInteger(1, priKeyD));//            log.Info(Hex.toHexString(priKeyD));//            ASN1ObjectIdentifier sm2DataOidPub = (ASN1ObjectIdentifier) pubSeq.getObjectAt(0);Asn1OctetString pubKeyX509 = (Asn1OctetString)pubSeq[1];X509Certificate x509 = (X509Certificate)new X509CertificateParser().ReadCertificate(pubKeyX509.GetOctets());sm2Cert.publicKey = x509.GetPublicKey();sm2Cert.certId = x509.SerialNumber.ToString(10); //这里转10进账,有啥其他进制要求的自己改改return sm2Cert;}catch (Exception e){//log.Error("readSm2File error: " + e.Message, e);return null;}}/**** @param cert* @return*/public static Sm2Cert ReadSm2X509Cert(byte[] cert){Sm2Cert sm2Cert = new Sm2Cert();try{X509Certificate x509 = new X509CertificateParser().ReadCertificate(cert);sm2Cert.publicKey = x509.GetPublicKey();sm2Cert.certId = x509.SerialNumber.ToString(10); //这里转10进账,有啥其他进制要求的自己改改return sm2Cert;}catch (Exception e){//log.Error("ReadSm2X509Cert error: " + e.Message, e);return null;}}public static byte[] ZeroIv(String algo){try{IBufferedCipher cipher = CipherUtilities.GetCipher(algo);int blockSize = cipher.GetBlockSize();byte[] iv = new byte[blockSize];Arrays.Fill(iv, (byte)0);return iv;}catch (Exception e){//log.Error("ZeroIv error: " + e.Message, e);return null;}}public static void Main2(string[] s){// 随便看看//log.Info("GMNamedCurves: ");foreach (string e in GMNamedCurves.Names){//log.Info(e);}//log.Info("sm2p256v1 n:" + x9ECParameters.N);//log.Info("sm2p256v1 nHex:" + Hex.ToHexString(x9ECParameters.N.ToByteArray()));// 生成公私钥对 ---------------------AsymmetricCipherKeyPair kp = GmUtil.GenerateKeyPair();//log.Info("private key d: " + ((ECPrivateKeyParameters)kp.Private).D);//log.Info("public key q:" + ((ECPublicKeyParameters)kp.Public).Q); //{x, y, zs...}//签名验签byte[] msg = System.Text.Encoding.UTF8.GetBytes("message digest");byte[] userId = System.Text.Encoding.UTF8.GetBytes("userId");byte[] sig = SignSm3WithSm2(msg, userId, kp.Private);//log.Info("testSignSm3WithSm2: " + Hex.ToHexString(sig));//log.Info("testVerifySm3WithSm2: " + VerifySm3WithSm2(msg, userId, sig, kp.Public));// 由d生成私钥 ---------------------BigInteger d = new BigInteger("097b5230ef27c7df0fa768289d13ad4e8a96266f0fcb8de40d5942af4293a54a", 16);ECPrivateKeyParameters bcecPrivateKey = GetPrivatekeyFromD(d);//log.Info("testGetFromD: " + bcecPrivateKey.D.ToString(16));//公钥X坐标PublicKeyXHex: 59cf9940ea0809a97b1cbffbb3e9d96d0fe842c1335418280bfc51dd4e08a5d4//公钥Y坐标PublicKeyYHex: 9a7f77c578644050e09a9adc4245d1e6eba97554bc8ffd4fe15a78f37f891ff8AsymmetricKeyParameter publicKey = GetPublickeyFromX509File(new FileInfo("d:/certs/69629141652.cer"));//log.Info(publicKey);AsymmetricKeyParameter publicKey1 = GetPublickeyFromXY(new BigInteger("59cf9940ea0809a97b1cbffbb3e9d96d0fe842c1335418280bfc51dd4e08a5d4", 16), new BigInteger("9a7f77c578644050e09a9adc4245d1e6eba97554bc8ffd4fe15a78f37f891ff8", 16));//log.Info("testReadFromX509File: " + ((ECPublicKeyParameters)publicKey).Q);//log.Info("testGetFromXY: " + ((ECPublicKeyParameters)publicKey1).Q);//log.Info("testPubKey: " + publicKey.Equals(publicKey1));//log.Info("testPubKey: " + ((ECPublicKeyParameters)publicKey).Q.Equals(((ECPublicKeyParameters)publicKey1).Q));// sm2 encrypt and decrypt test ---------------------AsymmetricCipherKeyPair kp2 = GenerateKeyPair();AsymmetricKeyParameter publicKey2 = kp2.Public;AsymmetricKeyParameter privateKey2 = kp2.Private;byte[] bs = Sm2Encrypt(System.Text.Encoding.UTF8.GetBytes("s"), publicKey2);//log.Info("testSm2Enc dec: " + Hex.ToHexString(bs));bs = Sm2Decrypt(bs, privateKey2);//log.Info("testSm2Enc dec: " + System.Text.Encoding.UTF8.GetString(bs));// sm4 encrypt and decrypt test ---------------------//0123456789abcdeffedcba9876543210 + 0123456789abcdeffedcba9876543210 -> 681edf34d206965e86b3e94f536e4246byte[] plain = Hex.Decode("0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210");byte[] key = Hex.Decode("0123456789abcdeffedcba9876543210");byte[] cipher = Hex.Decode("595298c7c6fd271f0402f804c33d3f66");bs = Sm4EncryptECB(key, plain, GmUtil.SM4_ECB_NOPADDING);//log.Info("testSm4EncEcb: " + Hex.ToHexString(bs)); ;bs = Sm4DecryptECB(key, bs, GmUtil.SM4_ECB_NOPADDING);//log.Info("testSm4DecEcb: " + Hex.ToHexString(bs));//读.sm2文件String sm2 = "MIIDHQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDW5/I9kZhObxXE9Vh1CzHdZhIhxn+3byBU\nUrzmGRKbDRMgI3hJKdvpqWkM5G4LNcIwggLNBgoqgRzPVQYBBAIBBIICvTCCArkwggJdoAMCAQIC\nBRA2QSlgMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu\nY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex\nMB4XDTE4MTEyNjEwMTQxNVoXDTIwMTEyNjEwMTQxNVowcjELMAkGA1UEBhMCY24xEjAQBgNVBAoM\nCUNGQ0EgT0NBMTEOMAwGA1UECwwFQ1VQUkExFDASBgNVBAsMC0VudGVycHJpc2VzMSkwJwYDVQQD\nDCAwNDFAWnRlc3RAMDAwMTAwMDA6U0lHTkAwMDAwMDAwMTBZMBMGByqGSM49AgEGCCqBHM9VAYIt\nA0IABDRNKhvnjaMUShsM4MJ330WhyOwpZEHoAGfqxFGX+rcL9x069dyrmiF3+2ezwSNh1/6YqfFZ\nX9koM9zE5RG4USmjgfMwgfAwHwYDVR0jBBgwFoAUa/4Y2o9COqa4bbMuiIM6NKLBMOEwSAYDVR0g\nBEEwPzA9BghggRyG7yoBATAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNuL3Vz\nL3VzLTE0Lmh0bTA4BgNVHR8EMTAvMC2gK6AphidodHRwOi8vdWNybC5jZmNhLmNvbS5jbi9TTTIv\nY3JsNDI4NS5jcmwwCwYDVR0PBAQDAgPoMB0GA1UdDgQWBBREhx9VlDdMIdIbhAxKnGhPx8FcHDAd\nBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDAYIKoEcz1UBg3UFAANIADBFAiEAgWvQi3h6\niW4jgF4huuXfhWInJmTTYr2EIAdG8V4M8fYCIBixygdmfPL9szcK2pzCYmIb6CBzo5SMv50Odycc\nVfY6";bs = Convert.FromBase64String(sm2);String pwd = "cfca1234";GmUtil.Sm2Cert sm2Cert = GmUtil.readSm2File(bs, pwd);//log.Info("testReadSm2File, pubkey: " + ((ECPublicKeyParameters)sm2Cert.publicKey).Q.ToString());//log.Info("testReadSm2File, prikey: " + Hex.ToHexString(((ECPrivateKeyParameters)sm2Cert.privateKey).D.ToByteArray()));//log.Info("testReadSm2File, certId: " + sm2Cert.certId);bs = Sm2Encrypt(System.Text.Encoding.UTF8.GetBytes("s"), ((ECPublicKeyParameters)sm2Cert.publicKey));//log.Info("testSm2Enc dec: " + Hex.ToHexString(bs));bs = Sm2Decrypt(bs, ((ECPrivateKeyParameters)sm2Cert.privateKey));//log.Info("testSm2Enc dec: " + System.Text.Encoding.UTF8.GetString(bs));msg = System.Text.Encoding.UTF8.GetBytes("message digest");userId = System.Text.Encoding.UTF8.GetBytes("userId");sig = SignSm3WithSm2(msg, userId, ((ECPrivateKeyParameters)sm2Cert.privateKey));//log.Info("testSignSm3WithSm2: " + Hex.ToHexString(sig));//log.Info("testVerifySm3WithSm2: " + VerifySm3WithSm2(msg, userId, sig, ((ECPublicKeyParameters)sm2Cert.publicKey)));}}
}

国密数字信封加密:

using CommonUtils;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.IO;
using System.Text;namespace Console民生银行国密数字信封
{internal class Program{static void Main(string[] args){try{//国密公钥证书string txtIsvPubCert = "MIICuzCCAl6gAwIBAgIFMAU2UREwDAYIKoEcz1UBg3UFADArMQswCQYDVQQGEwJDTjEcMBoGA1UECgwTQ0ZDQSBTTTIgVEVTVCBPQ0EyMTAeFw0yMDA1MTQxMDQ5MjNaFw0yMTA1MTQxMDQ5MjNaMHExCzAJBgNVBAYTAkNOMQ0wCwYDVQQKDARDTUJDMRIwEAYDVQQLDAlDTUJDX0RDTVMxGTAXBgNVBAsMEE9yZ2FuaXphdGlvbmFsLTExJDAiBgNVBAMMGzAzMDVAWjEyMzMyMjMxQDYzODA1MTQwMDFAMTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABEnvYQ89A2eFUsEvhfik13ehN1vNivpfk3UTCOPp209UbwLlatSu1aBFerNeNB6FQ9dgSSaXCiSMUfgKKL9wpBWjggElMIIBITAfBgNVHSMEGDAWgBTifrYQu5TrFeau0RUK/+jXoFc5nTBIBgNVHSAEQTA/MD0GCGCBHIbvKgICMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2ZjYS5jb20uY24vdXMvdXMtMTMuaHRtMGkGA1UdHwRiMGAwLqAsoCqGKGh0dHA6Ly8yMTAuNzQuNDIuMy9PQ0EyMS9TTTIvY3JsMzE1Ni5jcmwwLqAsoCqGKGh0dHA6Ly8yMTAuNzQuNDIuMy9PQ0EyMS9TTTIvY3JsMzE1Ni5jcmwwCwYDVR0PBAQDAgP4MB0GA1UdDgQWBBRXXvBVVWTiR6ARshCGukqaGyUKfzAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDAYIKoEcz1UBg3UFAANJADBGAiEA7O5rAx4+Alx2eIq0CHxTrNrQIOmphxShur4JDMwLiEYCIQDpJD/z85jWcwUeRAOOcstm4Ppl5zKu6pPohnbKm7gqvg==";//国密私钥证书string gmPrivateKeyCert = @"MIIDHwIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDBgkpL4SzxKcmNmrzvEtrl5lncqsy/fvW/6
uKSe2Wa5x0NeIcRuohTMdQIYd6rXsOAwggLPBgoqgRzPVQYBBAIBBIICvzCCArswggJeoAMCAQIC
BTAFNlERMAwGCCqBHM9VAYN1BQAwKzELMAkGA1UEBhMCQ04xHDAaBgNVBAoME0NGQ0EgU00yIFRF
U1QgT0NBMjEwHhcNMjAwNTE0MTA0OTIzWhcNMjEwNTE0MTA0OTIzWjBxMQswCQYDVQQGEwJDTjEN
MAsGA1UECgwEQ01CQzESMBAGA1UECwwJQ01CQ19EQ01TMRkwFwYDVQQLDBBPcmdhbml6YXRpb25h
bC0xMSQwIgYDVQQDDBswMzA1QFoxMjMzMjIzMUA2MzgwNTE0MDAxQDEwWTATBgcqhkjOPQIBBggq
gRzPVQGCLQNCAARJ72EPPQNnhVLBL4X4pNd3oTdbzYr6X5N1Ewjj6dtPVG8C5WrUrtWgRXqzXjQe
hUPXYEkmlwokjFH4Cii/cKQVo4IBJTCCASEwHwYDVR0jBBgwFoAU4n62ELuU6xXmrtEVCv/o16BX
OZ0wSAYDVR0gBEEwPzA9BghggRyG7yoCAjAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2Eu
Y29tLmNuL3VzL3VzLTEzLmh0bTBpBgNVHR8EYjBgMC6gLKAqhihodHRwOi8vMjEwLjc0LjQyLjMv
T0NBMjEvU00yL2NybDMxNTYuY3JsMC6gLKAqhihodHRwOi8vMjEwLjc0LjQyLjMvT0NBMjEvU00y
L2NybDMxNTYuY3JsMAsGA1UdDwQEAwID+DAdBgNVHQ4EFgQUV17wVVVk4kegEbIQhrpKmhslCn8w
HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMAwGCCqBHM9VAYN1BQADSQAwRgIhAOzuawMe
PgJcdniKtAh8U6za0CDpqYcUobq+CQzMC4hGAiEA6SQ/8/OY1nMFHkQDjnLLZuD6ZecyruqT6IZ2
ypu4Kr4=";//国密私钥证书密码string gmPrivateKeyCertPwd = "123abc";string inputStr = "oh yeah 123。";Console.WriteLine("数字信封-待加密数据:" + inputStr);var aa = CmsEncrypt(inputStr, txtIsvPubCert);Console.WriteLine("数字信封-加密数据:" + aa);//测试JAVA程序加密结果//  aa = "MIIBdgYKKoEcz1UGAQQCA6CCAWYwggFiAgECMYGdMIGaAgECgBRXXvBVVWTiR6ARshCGukqaGyUKfzANBgkqgRzPVQGCLQMFAARwV3Je52JbokQWyl7uD2GDuWtkyzIzn8nsE2uX2XthHTwnoB+WtLkemKjs+iUJl5EShTgUI2g5N6Uw3/4QN9/LHETixsQ/Emv3hW9W3V2ERNJiu9Hd/raPN+zIyWhraEU6oXTvpwfFgNoVjbp9U9UbHjCBvAYKKoEcz1UGAQQCATAbBgcqgRzPVQFoBBArCB1Z2eO/rLl1Xdn9MMIOgIGQKNpS0jslyLvneyGs2xI2Rz4dLiEznOeXt8JjuEo08vKNf0JOpxFIK9q8X8oCE/NCBjwSmZ38ZwauUydQE+3zQUiInqIOPV8+w+1ejmi4LyWTeDuWtYUESw7qfmG7oxd6hvIM3pswNXXxOEjfmH/7cS8iHCzRg2fli0smDOREil8lPjUM68lTZ9xgcsq4a8+2";                // Console.WriteLine("数字信封-加密数据:" + aa);var bb = CmsDecrypt(aa, gmPrivateKeyCert, gmPrivateKeyCertPwd);Console.WriteLine("数字信封-解密数据:" + bb);}catch (Exception ex){Console.WriteLine("ex:" + ex.Message);}Console.ReadKey();}/// <summary>/// 数字信封-加密/// </summary>static string CmsEncrypt(string inputStr, string pubkeyCert){//SM2,SM4 加密数据从 string 转 byte  数组时,使用UTF8。Encoding enc = Encoding.UTF8;//调试时可以写死KEY和IV//string sm4key = "1234567890123456";//string sm4iv = "1234567890123456";//随机生成KEY和IV,都是16字符长度string sm4key = Guid.NewGuid().ToString("N").Substring(0, 16);string sm4iv = Guid.NewGuid().ToString("N").Substring(0, 16);//如果是 NoPadding, SM4 加密的数据(byte[] plain)得是16的倍数string algo = "SM4/CBC/PKCS7Padding";byte[] keyBytes = Encoding.UTF8.GetBytes(sm4key);byte[] ivBytes = Encoding.UTF8.GetBytes(sm4iv);#region 使用SM2加密SM4 KEYAsymmetricKeyParameter bankPubCert2 = GmUtil.GetPublickeyFromX509String(pubkeyCert);byte[] bytencryptedData = enc.GetBytes(sm4key);byte[] bysm4keyEncrypted = GmUtil.Sm2EncryptOld(bytencryptedData, bankPubCert2);//使用SM2加密SM4 KEY//BC库SM2加密结果会带04,删除加密结果前的04,否则JAVA(民生银行) 那边解密不了。Invalid point encodingstring newCipherText = Hex.ToHexString(bysm4keyEncrypted);if (newCipherText.StartsWith("04")){newCipherText = newCipherText.Substring(2);}bysm4keyEncrypted = Hex.Decode(newCipherText);// 主题密钥标识符 (SKI)。 JAVA BC 库写法:SubjectKeyIdentifier sid = cert.getSubjectKeyIdentifier();             SubjectKeyIdentifier sid = new Org.BouncyCastle.X509.Extension.SubjectKeyIdentifierStructure(bankPubCert2);#endregionAsn1OctetString encKey = new DerOctetString(bysm4keyEncrypted);AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(new DerObjectIdentifier("1.2.156.10197.1.301.3")); //sm2  公钥加密算法IDKeyTransRecipientInfo ktr = new KeyTransRecipientInfo(new RecipientIdentifier(new DerOctetString(sid.GetKeyIdentifier())), keyEncAlg, encKey);RecipientInfo ri = new RecipientInfo(ktr); //SM4 key 使用SM2加密后,放入RecipientInfo。byte[] sourceData = enc.GetBytes(inputStr);byte[] encryptedData = GmUtil.Sm4EncryptCBC(keyBytes, sourceData, ivBytes, algo); ;//待SM4 加密,加密后的数据DerObjectIdentifier sm2DataID = new DerObjectIdentifier("1.2.156.10197.6.1.4.2.1");//SM2数据类型data IDDerOctetString doct = new DerOctetString(ivBytes);DerObjectIdentifier tOID = new DerObjectIdentifier("1.2.156.10197.1.104"); // SM4_CBC ,ID AlgorithmIdentifier contentEncryptionAlgId = new AlgorithmIdentifier(tOID, doct);//用 AlgorithmIdentifier携带 IV 数据Asn1EncodableVector recipientInfos = new Asn1EncodableVector();recipientInfos.Add(ri);//SM4 key 使用SM2加密后,放入RecipientInfo。Asn1Set aSet1 = null;Asn1OctetString encryptOctet = new BerOctetString(encryptedData);//SM4加密后的数据EncryptedContentInfo eci = new EncryptedContentInfo(sm2DataID, contentEncryptionAlgId, encryptOctet);// SM4-IV 和 SM4加密后的数据EnvelopedData enData = new EnvelopedData(null, new DerSet(recipientInfos), eci, aSet1);// SM4-KEY,SM4-IV 和 SM4加密后的数据DerObjectIdentifier sm2EnvelopedDataID = new DerObjectIdentifier("1.2.156.10197.6.1.4.2.3");//数字信封数据类型envelopedData IDContentInfo contentInfo = new ContentInfo(sm2EnvelopedDataID, enData);string base64CmsEncryptData = string.Empty;using (MemoryStream bos = new MemoryStream()){using (DerOutputStream dos = new DerOutputStream(bos)){dos.WriteObject((new CmsEnvelopedData(contentInfo)).ContentInfo);byte[] byteArray = bos.ToArray();base64CmsEncryptData = Convert.ToBase64String(byteArray);}}return base64CmsEncryptData;}/// <summary>/// 数字信封-解密/// </summary>static string CmsDecrypt(string cmsEnvelopedData, string gmPrivateKeyCert, string gmPrivateKeyCertPwd){//获取私钥对象 AsymmetricKeyParameter privateKeyvar pemPrivateKey = Convert.FromBase64String(gmPrivateKeyCert);var tempPrivateKey = GmUtil.readSm2File(pemPrivateKey, gmPrivateKeyCertPwd);AsymmetricKeyParameter privateKey = tempPrivateKey.privateKey;byte[] bEnvelop = Convert.FromBase64String(cmsEnvelopedData);CmsEnvelopedData cmsEnData = new CmsEnvelopedData(bEnvelop);ContentInfo info = cmsEnData.ContentInfo;EnvelopedData enData = EnvelopedData.GetInstance(info.Content);Asn1Set receivers = enData.RecipientInfos;#region 使用SM2 私钥解密出SM4 KEY //没适配多receivers的情况,强取第1个RecipientInfo recipientInfo = RecipientInfo.GetInstance(receivers[0]);KeyTransRecipientInfo keyTransRecipientInfo = KeyTransRecipientInfo.GetInstance(recipientInfo.Info);Asn1OctetString encryptKey = keyTransRecipientInfo.EncryptedKey;AlgorithmIdentifier algId = keyTransRecipientInfo.KeyEncryptionAlgorithm;byte[] byEncryptKey = encryptKey.GetOctets();string hexEncryptKey = Hex.ToHexString(byEncryptKey);//BC库解密,密文前要加 “04”,否则会报 Invalid point encoding XXif (!hexEncryptKey.StartsWith("04")){hexEncryptKey = "04" + hexEncryptKey;}byEncryptKey = Hex.Decode(hexEncryptKey);byte[] symmetricKey = GmUtil.Sm2DecryptOld(byEncryptKey, privateKey);#endregion#region 拿到 SM4 IV//拿到 SM4 IVEncryptedContentInfo data = enData.EncryptedContentInfo;AlgorithmIdentifier symmetricAlgId = data.ContentEncryptionAlgorithm;DerOctetString doct = (DerOctetString)symmetricAlgId.Parameters;byte[] bySm4Iv = doct.GetOctets();#endregion//取出被SM4加密的数据string algo = "SM4/CBC/PKCS7Padding";byte[] encryptedBytes = data.EncryptedContent.GetOctets();byte[] sourceData = GmUtil.Sm4DecryptCBC(symmetricKey, encryptedBytes, bySm4Iv, algo);string sm4DecryptedStr = Encoding.UTF8.GetString(sourceData);//偷懒,写死了UTF8。return sm4DecryptedStr;}}
}

end

C#.NET 国密数字信封 民生银行相关推荐

  1. 国密数字证书离线申请流程-国密数字信封解析

    国密数字证书离线申请流程-国密数字信封解析 背景 我们设备本身已经集成了自建CA服务器,并且支持国密/商密,但在实际上线使用过程中发现虽然大多数局点都是仅仅使用国密功能,主要涉及国密证书生成,国密ss ...

  2. 国密双证书签发及国密数据信封解析

    国密双证书的签发,及国密数字信封解析 产生签名密钥对 gmssl ecparam -genkey -name sm2p256v1 -text -out server_sign.key 产生p10签名请 ...

  3. 赋能金融领域,国密改造让安全合规更加牢固

    随着中国人民银行关于推进国产密码在金融领域应用的实施方案的不断深入,以及信创生态持续健全.技术环境日趋成熟,基于国产商用密码的数字证书已在银行.证券.保险等多领域加速普及,国密改造成为银行和金融机构筑 ...

  4. Apache APISIX 玩转 Tongsuo 国密插件

    文|罗泽轩 Apache APISIX PMC 本文通过解读国密的相关内容与标准,呈现了当下国内技术环境中对于国密功能支持的现状.并从 API 网关 Apache APISIX 的角度,带来有关国密的 ...

  5. GmSSL制作国密算法自签证书和 HTTPS 配置

    GmSSL 是一个开源(遵循 BSD 协议)的密码工具箱,支持 SM2 / SM3 / SM4 / SM9 / ZUC 等国密(国家商用密码)算法.SM2 国密数字证书及基于 SM2 证书的 SSL ...

  6. php gmssl,支持国密SM2/SM3/SM4/SM9/ZUC/SSL的密码工具箱GmSSL

    GmSSL概述 GmSSL是一个开源的密码工具箱,支持SM2/SM3/SM4/SM9/ZUC等国密(国家商用密码)算法.SM2国密数字证书及基于SM2证书的SSL/TLS安全通信协议,支持国密硬件密码 ...

  7. 国密改造——银行系统国产密码改造应用

    一.何为国密改造? 2012年,工信部和公安部通告了RSA1024算法被破解的风险,为保证金融行业各基础信息系统安全,中国人民银行要求各银行对网上银行等信息系统进行国产密码算法改造.2012年,中国人 ...

  8. 安全算法 - 国密算法

    国密即国家密码局认定的国产密码算法.主要有SM1,SM2,SM3,SM4,SM7, SM9. 国密算法分类 国家标准官方网站如下:http://openstd.samr.gov.cn/bzgk/gb/ ...

  9. 商密圈大咖齐聚北京 共商国密开源未来发展方向

    2019年7月26日,星期五,北京已经连续被骄阳炙烤了一周,最高温度甚至超过 了40摄氏度.但是相比室外的高温,商密圈精英们参与"基于国密算法的OpenSSL技术沙龙"的热情更高, ...

最新文章

  1. C#中利用委托实现多线程跨线程操作
  2. ASP.Net string 类的扩展方法 [转]
  3. [Golang]计算一个文件的MD5值
  4. mmdetection 使用笔记 01: 安装与简单的推理demo
  5. leetcode - 198. 打家劫舍
  6. 一个RSS阅读器的源码,不敢独享!
  7. Java并发编程-八锁问题带你彻底理解对象锁和类锁
  8. 如下哪个是Java中的合法自定义标识符_吉大13春《面向对象程序设计》在线作业答案...
  9. 苹果笔记本安装Win10双系统+分区教程《完整精华版》
  10. 工作流引擎 开发框架 java源码 电商源码
  11. ECSHOP二次开发之心得体验
  12. java 微博发布时间_java 对新浪微博微博的发表时间解析
  13. Math常用的数学运算(包括取整、取绝对值、保留几位小数等)
  14. 视觉十四讲 第三讲 深蓝学院习题分析
  15. 阿里巴巴2018秋招面经之前端岗(1~5面)
  16. Python和VizViewer进行自动驾驶数据集可视化
  17. 论文参考文献GB/T 7714格式生成
  18. 解决树莓派鼠标延迟/迟滞问题-转CSDN博主“Deiki”-sunziren
  19. 案例分析第一课预习笔记
  20. Visio Viewer 无法打开 VSD文件

热门文章

  1. 日冕物质抛射检测matlab,中国科学技术大学 日冕物质抛射研究取得重要进展
  2. 编程之美 一摞烙饼问题
  3. 2021-12-06 自动化专业C语言上机作业参考答案04
  4. Git巧用贮藏避免解决冲突时的合并记录
  5. 玉米社:抖音短视频一般制作多长时间最好?为什么?
  6. 立体字3D字体数字设计|造型艺术字,灵感来源,速码!
  7. ADI家的DSP和TI家的DSP有什么区别,我的一些不成熟的看法如下
  8. java上下文控制,Esper事件处理引擎_8_EPL 语法_2_Context 上下文_2_条件控制
  9. 系统服务器是什么意思,什么是服务器
  10. linux socket errno 4,解决socket.error: [Errno 98] Address already in use问题