예제 #1
0
        /// <summary>
        /// 签名验证算法
        /// 利用签名者的公钥和传过来的r,s来验证签名是否合法
        /// </summary>
        /// <param name="sm2">sm2对象</param>
        /// <param name="ppk">签名者的公钥16进制字符串</param>
        /// <param name="Z">签名算法产生的16进制字符串 Z</param>
        /// <param name="r">签名算法生成的 R</param>
        /// <param name="s">签名算法生成的 S</param>
        /// <returns></returns>
        public bool Signature_Check(SM2 sm2, string ppk, string Z, string r, string s)
        {
            ECPoint test_p = null;

            //test_p = sm2.userKey;
            //MessageBox.Show(ppk);
            byte[] key = strToToHexByte(ppk);

            test_p = sm2.ecc_curve.DecodePoint(key);
            Com.Itrus.Crypto.SM2.SM2Result sm2Ret = new Com.Itrus.Crypto.SM2.SM2Result();//实例化一个SM2Result的对象sm2Ret
            SM3Digest sm3 = new SM3Digest();

            byte[] z = strToToHexByte(Z);
            sm3.BlockUpdate(z, 0, z.Length);
            byte[] md = new byte[32];
            sm3.DoFinal(md, 0);

            sm2Ret.r = new BigInteger(r, 16);
            sm2Ret.s = new BigInteger(s, 16);
            sm2.Sm2Verify(md, test_p, sm2Ret.r, sm2Ret.s, sm2Ret); //调用Sm2Verify方法,得到R

            if (sm2Ret.r.Equals(sm2Ret.R))                         //如果r==R
            {
                return(true);                                      //System.Console.Out.WriteLine("\n签名结果验证通过!r == R\n");
            }
            else//r!=R
            {
                return(false);//System.Console.Out.WriteLine("\n签名结果验证失败!r != R\n");
            }
        }
예제 #2
0
        /// <summary>
        /// 公钥加密算法,利用下一个用户的公钥进行加密
        /// </summary>
        /// <param name="sm2">sm2对象</param>
        /// <param name="msg">要加密的消息</param>
        /// <param name="ppk">目的签名者的公钥文件夹路径</param>
        /// <param name="id">加密后消息存入的文件夹(最好不存起来)</param>
        public string[] Test_sm2_cipher(SM2 sm2, string msg, string ppk)
        {
            byte[] data = Encoding.Default.GetBytes(msg);//将信息转化为比特

            // 加密过程
            ECPoint userKey = null;

            byte[] key = null;

            ReadpublicKey(out key, ppk);              //读取解密者的公钥
            userKey = sm2.ecc_curve.DecodePoint(key); //把字节形的转化为Ecpoint
            System.String sdata = new UTF8Encoding().GetString(Hex.Encode(data));

            SM2.Cipher cipher = new SM2.Cipher();
            ECPoint    c1     = cipher.Init_enc(sm2, userKey); //调用Init_enc方法

            byte[]        bc1  = c1.GetEncoded();              //将c1的数据类型转换成比特串
            System.String sbc1 = new UTF8Encoding().GetString(Hex.Encode(bc1));

            cipher.Encrypt(data);

            System.String sc2 = new UTF8Encoding().GetString(Hex.Encode(data));
            byte[]        c3  = new byte[32];
            cipher.Dofinal(c3);
            System.String sc3 = new UTF8Encoding().GetString(Hex.Encode(c3));

            string[] cc = { sbc1, sc2, sc3 };
            return(cc);
        }
예제 #3
0
        /// <summary>
        /// 数字签名算法
        /// 利用自己的公私钥生成rs并存入文件
        /// </summary>
        /// <param name="sm2">sm2对象</param>
        /// <param name="pripk">自己的私钥文件夹</param>
        /// <param name="ppk">自己的公钥文件路径</param>
        /// <param name="ida">用户名</param>
        public string Test_sm2_sign(SM2 sm2, string pripk, string ppk, string ida)
        {
            BigInteger test_d = null;
            ECPoint    test_p = null;

            byte[] key = null;

            //读取私钥
            Readprikey(out test_d, pripk);
            //读取公钥
            ReadpublicKey(out key, ppk);
            test_p = sm2.ecc_curve.DecodePoint(key);

            Com.Itrus.Crypto.SM2.SM2Result sm2Ret = new Com.Itrus.Crypto.SM2.SM2Result();//实例化一个SM2Result的对象sm2Ret
            SM3Digest sm3 = new SM3Digest();

            byte[] z = sm2.Sm2GetZ(Encoding.Default.GetBytes(ida), test_p);//调用Sm2GetZ方法求a的Z的字节数组

            sm3.BlockUpdate(z, 0, z.Length);

            byte[] md = new byte[32];
            sm3.DoFinal(md, 0);
            sm2.Sm2Sign(md, test_d, test_p, sm2Ret);          //生成rs
            Writers(sm2Ret.r, sm2Ret.s, ida + "rs" + ".txt"); //写入rs文件

            return(byteToHexStr(z));
        }
예제 #4
0
    public static bool VerifySign(string _dataToSign, string _signPack, string _pubKeyX, string _pubKeyY)
    {
        SM2 sm2 = SM2.Instance;

        sm2.cipher_sm = new SM2.Cipher();

        string signPack1 = _signPack.Substring(0, 64);
        string signPack2 = _signPack.Substring(64, 64);

        BigInteger ecc_gx = new BigInteger(_pubKeyX, 16);
        BigInteger ecc_gy = new BigInteger(_pubKeyY, 16);

        ECFieldElement ecc_gx_fieldElement = sm2.ecc_curve.FromBigInteger(ecc_gx);                                 //选定椭圆曲线上基点 G 的 x 坐标
        ECFieldElement ecc_gy_fieldElement = sm2.ecc_curve.FromBigInteger(ecc_gy);                                 //选定椭圆曲线上基点 G 的 y 坐标
        ECPoint        ecc_point_g         = new FpPoint(sm2.ecc_curve, ecc_gx_fieldElement, ecc_gy_fieldElement); //生成基点 G

        SM2.SM2Result result = new SM2.SM2Result();

        BigInteger gp_br = new BigInteger(signPack1, 16);
        BigInteger gp_bs = new BigInteger(signPack2, 16);

        result.r = gp_br;
        result.s = gp_bs;

        sm2.Sm2Verify(HexStringToBytes(_dataToSign), ecc_point_g, gp_br, gp_bs, result);

        return(result.R.Equals(result.r));
    }
예제 #5
0
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="privateKey">Base64进制表示的私钥</param>
        /// <param name="text">Base64进制表示的密文</param>
        /// <returns></returns>
        public static string Decrypt(string privateKey, string text)
        {
            if (null == privateKey || null == text)
            {
                return(null);
            }

            byte[] privateKeyByte = Convert.FromBase64String(privateKey);

            if (null == privateKeyByte || privateKeyByte.Length == 0)
            {
                return(null);
            }

            byte[] encryptedData = Convert.FromBase64String(text);

            if (encryptedData == null || encryptedData.Length == 0)
            {
                return(null);
            }

            String data = Encoding.ASCII.GetString(Hex.Encode(encryptedData));

            //采用C1C2C3模式
            //byte[] c1Bytes = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(0, 130)));
            //int c2Len = encryptedData.Length - 97;
            //byte[] c2 = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(130, 2 * c2Len)));
            //byte[] c3 = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(130 + 2 * c2Len, 64)));

            //采用C1C3C2模式
            byte[] c1Bytes = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(0, 130)));
            int    c2Len   = encryptedData.Length - 97;

            byte[] c3 = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(130, 64)));
            byte[] c2 = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(130 + 64, 2 * c2Len)));

            SM2        sm2   = SM2.Instance;
            BigInteger userD = new BigInteger(1, privateKeyByte);

            ECPoint c1     = sm2.ecc_curve.DecodePoint(c1Bytes);
            Cipher  cipher = new Cipher();

            cipher.Init_dec(userD, c1);
            cipher.Decrypt(c2);
            cipher.Dofinal(c3);

            return(Encoding.UTF8.GetString(c2));
        }
예제 #6
0
        private static void SM2_Sample()
        {
            var sm2Engine = new SM2();

            var parameter = sm2Engine.KeyGenerator();

            var pub  = AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPublicKey(parameter.PublicKey);
            var priv = AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(parameter.PrivateKey);

            var p = sm2Engine.Encrypt("hello sm2", (ECPublicKeyParameters)pub);


            var decStr = sm2Engine.Decrypt(p, (ECPrivateKeyParameters)priv);

            Console.WriteLine(decStr);
        }
예제 #7
0
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="publicKey">Base64表示的公钥</param>
        /// <param name="text">需要加密的明文</param>
        /// <returns>返回Base64表示的密文</returns>
        public static string Encrypt(string publicKey, string text)
        {
            if (null == publicKey || null == text)
            {
                return(null);
            }

            byte[] publicKeyByte = Convert.FromBase64String(publicKey);

            if (null == publicKeyByte || publicKeyByte.Length == 0)
            {
                return(null);
            }

            byte[] data = System.Text.Encoding.UTF8.GetBytes(text);

            if (data == null || data.Length == 0)
            {
                return(null);
            }

            byte[] source = new byte[data.Length];
            Array.Copy(data, 0, source, 0, data.Length);

            Cipher cipher = new Cipher();
            SM2    sm2    = SM2.Instance;

            ECPoint userKey = sm2.ecc_curve.DecodePoint(publicKeyByte);

            ECPoint c1 = cipher.Init_enc(sm2, userKey);

            cipher.Encrypt(source);

            byte[] c3 = new byte[32];
            cipher.Dofinal(c3);

            string sc1 = Encoding.ASCII.GetString(Hex.Encode(c1.GetEncoded()));
            string sc2 = Encoding.ASCII.GetString(Hex.Encode(source));
            string sc3 = Encoding.ASCII.GetString(Hex.Encode(c3));

            //string hexStr = (sc1 + sc2 + sc3).ToUpper(); //采用C1C2C3模式
            string hexStr = sc1 + sc3 + sc2; //采用C1C3C2模式

            byte[] bytes = Hex.Decode(hexStr);

            return(Convert.ToBase64String(bytes));
        }
예제 #8
0
        /// <summary>
        /// 私钥解密算法
        /// </summary>
        /// <param name="sm2">sm2对象</param>
        /// <param name="pripk">自己的私钥</param>
        /// <param name="id">id</param>
        /// <returns></returns>
        public string deciphering(SM2 sm2, string pripk, string mc1, string sc2, string mc3)
        {
            byte[]     bc1   = new byte[32];
            BigInteger userD = null;

            // String sc2 = null;
            byte[] c3 = new byte[32];
            byte[] data;
            bc1 = strToToHexByte(mc1);
            c3  = strToToHexByte(mc3);
            Readprikey(out userD, pripk);
            // 解密过程
            SM2.Cipher cipher = new SM2.Cipher();
            cipher = new SM2.Cipher();

            ECPoint c1 = sm2.ecc_curve.DecodePoint(bc1);

            data = strToToHexByte(sc2);

            cipher.Init_dec(userD, c1); //调用Init_dec,从c中取出比特串c1,将c1的数据类型转化为椭圆曲线上的点,如果不满足椭圆曲线上的点则报错。
            cipher.Decrypt(data);       //调用Decrypt方法
            //System.String sdata1 = new UTF8Encoding().GetString(Hex.Encode(data));

            string sdata = System.Text.Encoding.Default.GetString(data);

            System.String sc3 = new UTF8Encoding().GetString(Hex.Encode(c3));

            byte[] c3_ = new byte[32];
            cipher.Dofinal(c3_);
            System.String sc3_ = new UTF8Encoding().GetString(Hex.Encode(c3_));
            //数据校验检测数据是否被篡改或丢失
            if (sc3_.ToUpper().Equals(sc3.ToUpper()))//sc3_==sc3
            {
                return(sdata);
            }
            else
            {
                // System.Console.Out.WriteLine("数据校验失败!\n");//sc3_!=sc3
                return("0");
            }
        }
예제 #9
0
        public static void GenerateKeyPair(ResultKey ret)
        {
            SM2 sm2 = SM2.Instance;
            AsymmetricCipherKeyPair key    = sm2.ecc_key_pair_generator.GenerateKeyPair();
            ECPrivateKeyParameters  ecpriv = (ECPrivateKeyParameters)key.Private;
            ECPublicKeyParameters   ecpub  = (ECPublicKeyParameters)key.Public;
            BigInteger privateKey          = ecpriv.D;
            ECPoint    publicKey           = ecpub.Q;

            System.Console.Out.WriteLine("公钥: " + Hex.ToHexString(publicKey.GetEncoded()).ToUpper());
            System.Console.Out.WriteLine("私钥: " + Hex.ToHexString(privateKey.ToByteArray()).ToUpper());

            //System.Console.Out.WriteLine("公钥: " + Encoding.ASCII.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper());
            //System.Console.Out.WriteLine("私钥: " + Encoding.ASCII.GetString(Hex.Encode(privateKey.ToByteArray())).ToUpper());


            ret.bytePubkey      = publicKey.GetEncoded();
            ret.bytePrikey      = privateKey.ToByteArray();
            ret.base64StrPubkey = Convert.ToBase64String(ret.bytePubkey);
            ret.base64StrPrikey = Convert.ToBase64String(ret.bytePrikey);
        }
예제 #10
0
        /// <summary>
        /// 生成公私钥,并存入文件夹
        /// </summary>
        /// <param name = "pripk">私钥文件地址</param>
        /// <param name="ppk">公钥文件地址</param>
        /// <param name="sm2">传入的SM2实例</param>

        public void Creatkey(SM2 sm2, string pripk, string ppk)
        {
            AsymmetricCipherKeyPair keypair = null;

            for (int i = 0; i < 1; i++)
            {
                keypair = sm2.ecc_key_pair_generator.GenerateKeyPair();
            }

            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keypair.Private;
            ECPublicKeyParameters  ecpub  = (ECPublicKeyParameters)keypair.Public;

            BigInteger userD = ecpriv.D;
            ECPoint    Key   = ecpub.Q;

            byte[]        userKeybyte = Key.GetEncoded();
            System.String userKey     = new UTF8Encoding().GetString(Hex.Encode(userKeybyte));

            string avg = Console.ReadLine();

            WriterKey(userD, pripk); //私钥
            WriterKey(userKey, ppk); //公钥
        }
예제 #11
0
 public void setup(SM2 memCommand2)
 {
     _memCommand2 = memCommand2;
 }
예제 #12
0
        /// <summary>
        /// Try get algorithm from mechanism.
        /// </summary>
        /// <param name="mechanism">Algorithm mechanism.</param>
        /// <param name="algorithm">Algorithm.</param>
        /// <returns></returns>
        public static bool TryGetAlgorithm(string mechanism, out ISignatureAlgorithm algorithm)
        {
            mechanism = mechanism.Replace('_', '-').ToUpperInvariant();
            switch (mechanism)
            {
            case "SHA1WITHCVC-ECDSA":
            case "SHA-1WITHCVC-ECDSA": algorithm = SHA1withCVC_ECDSA; return(true);

            case "SHA224WITHCVC-ECDSA":
            case "SHA-224WITHCVC-ECDSA": algorithm = SHA224withCVC_ECDSA; return(true);

            case "SHA256WITHCVC-ECDSA":
            case "SHA-256WITHCVC-ECDSA": algorithm = SHA256withCVC_ECDSA; return(true);

            case "SHA384WITHCVC-ECDSA":
            case "SHA-384WITHCVC-ECDSA": algorithm = SHA384withCVC_ECDSA; return(true);

            case "SHA512WITHCVC-ECDSA":
            case "SHA-512WITHCVC-ECDSA": algorithm = SHA512withCVC_ECDSA; return(true);

            case "ED25519": algorithm = new Ed25519(); return(true);

            case "ED25519CTX": algorithm = new Ed25519ctx(); return(true);

            case "ED25519PH": algorithm = new Ed25519ph(); return(true);

            case "ED448": algorithm = new Ed448(); return(true);

            case "ED448PH": algorithm = new Ed448ph(); return(true);

            case "GOST3411WITHECGOST3410":
            case "ECGOST3410":
            case "ECGOST3410-2001":
            case "ECGOST-3410":
            case "ECGOST-3410-2001": algorithm = GOST3411withECGOST3410; return(true);

            case "GOST3411WITHGOST3410":
            case "GOST3410":
            case "GOST3410-94":
            case "GOST-3410":
            case "GOST-3410-94": algorithm = GOST3411withGOST3410; return(true);

            case "RIPEMD160WITHPLAIN-ECDSA":
            case "RIPEMD-160WITHPLAIN-ECDSA": algorithm = RIPEMD160withPLAIN_ECDSA; return(true);

            case "SHA1WITHPLAIN-ECDSA":
            case "SHA-1WITHPLAIN-ECDSA": algorithm = SHA1withPLAIN_ECDSA; return(true);

            case "SHA224WITHPLAIN-ECDSA":
            case "SHA-224WITHPLAIN-ECDSA": algorithm = SHA224withPLAIN_ECDSA; return(true);

            case "SHA256WITHPLAIN-ECDSA":
            case "SHA-256WITHPLAIN-ECDSA": algorithm = SHA256withPLAIN_ECDSA; return(true);

            case "SHA384WITHPLAIN-ECDSA":
            case "SHA-384WITHPLAIN-ECDSA": algorithm = SHA384withPLAIN_ECDSA; return(true);

            case "SHA512WITHPLAIN-ECDSA":
            case "SHA-512WITHPLAIN-ECDSA": algorithm = SHA512withPLAIN_ECDSA; return(true);

            case "PSSWITHRSA": algorithm = PSSwithRSA; return(true);

            case "SHA1WITHDSA":
            case "SHA-1WITHDSA": algorithm = SHA1withDSA; return(true);

            case "SHA224WITHDSA":
            case "SHA-224WITHDSA": algorithm = SHA224withDSA; return(true);

            case "SHA256WITHDSA":
            case "SHA-256WITHDSA": algorithm = SHA256withDSA; return(true);

            case "SHA384WITHDSA":
            case "SHA-384WITHDSA": algorithm = SHA384withDSA; return(true);

            case "SHA512WITHDSA":
            case "SHA-512WITHDSA": algorithm = SHA512withDSA; return(true);

            case "SHA3-224WITHDSA":
            case "SHA-3-224WITHDSA": algorithm = SHA3_224withDSA; return(true);

            case "SHA3-256WITHDSA":
            case "SHA-3-256WITHDSA": algorithm = SHA3_256withDSA; return(true);

            case "SHA3-384WITHDSA":
            case "SHA-3-384WITHDSA": algorithm = SHA3_384withDSA; return(true);

            case "SHA3-512WITHDSA":
            case "SHA-3-512WITHDSA": algorithm = SHA3_512withDSA; return(true);

            case "SHA1WITHECDSA":
            case "SHA-1WITHECDSA": algorithm = SHA1withECDSA; return(true);

            case "SHA224WITHECDSA":
            case "SHA-224WITHECDSA": algorithm = SHA224withECDSA; return(true);

            case "SHA256WITHECDSA":
            case "SHA-256WITHECDSA": algorithm = SHA256withECDSA; return(true);

            case "SHA384WITHECDSA":
            case "SHA-384WITHECDSA": algorithm = SHA384withECDSA; return(true);

            case "SHA512WITHECDSA":
            case "SHA-512WITHECDSA": algorithm = SHA512withECDSA; return(true);

            case "SHA3-224WITHECDSA":
            case "SHA-3-224WITHECDSA": algorithm = SHA3_224withECDSA; return(true);

            case "SHA3-256WITHECDSA":
            case "SHA-3-256WITHECDSA": algorithm = SHA3_256withECDSA; return(true);

            case "SHA3-384WITHECDSA":
            case "SHA-3-384WITHECDSA": algorithm = SHA3_384withECDSA; return(true);

            case "SHA3-512WITHECDSA":
            case "SHA-3-512WITHECDSA": algorithm = SHA3_512withECDSA; return(true);

            case "MD2WITHRSA": algorithm = MD2withRSA; return(true);

            case "MD5WITHRSA": algorithm = MD5withRSA; return(true);

            case "RIPEMD128WITHRSA":
            case "RIPEMD-128WITHRSA": algorithm = RIPEMD128withRSA; return(true);

            case "RIPEMD160WITHRSA":
            case "RIPEMD-160WITHRSA": algorithm = RIPEMD160withRSA; return(true);

            case "RIPEMD256WITHRSA":
            case "RIPEMD-256WITHRSA": algorithm = RIPEMD256withRSA; return(true);

            case "SHA1WITHRSA":
            case "SHA-1WITHRSA": algorithm = SHA1withRSA; return(true);

            case "SHA224WITHRSA":
            case "SHA-224WITHRSA": algorithm = SHA224withRSA; return(true);

            case "SHA256WITHRSA":
            case "SHA-256WITHRSA": algorithm = SHA256withRSA; return(true);

            case "SHA384WITHRSA":
            case "SHA-384WITHRSA": algorithm = SHA384withRSA; return(true);

            case "SHA512WITHRSA":
            case "SHA-512WITHRSA": algorithm = SHA512withRSA; return(true);

            case "SHA3-224WITHRSA":
            case "SHA-3-224WITHRSA": algorithm = SHA3_224withRSA; return(true);

            case "SHA3-256WITHRSA":
            case "SHA-3-256WITHRSA": algorithm = SHA3_256withRSA; return(true);

            case "SHA3-384WITHRSA":
            case "SHA-3-384WITHRSA": algorithm = SHA3_384withRSA; return(true);

            case "SHA3-512WITHRSA":
            case "SHA-3-512WITHRSA": algorithm = SHA3_512withRSA; return(true);

            case "SHA1WITHRSAANDMGF1":
            case "SHA-1WITHRSAANDMGF1": algorithm = PSSwithRSA; return(true);

            case "SHA256WITHSM2":
            case "SHA-256WITHSM2": algorithm = SHA256withSM2; return(true);

            case "SM3WITHSM2": algorithm = SM3withSM2; return(true);

            default: break;
            }
            string prefix;
            string suffix;
            int    index = mechanism.IndexOf("WITH");

            if (index >= 0)
            {
                prefix = mechanism.Substring(0, index);
                suffix = mechanism.Substring(index + 4, mechanism.Length - index - 4);
            }
            else
            {
                prefix = string.Empty;
                suffix = mechanism;
            }
            if (suffix == "ELGAMAL")
            {
                algorithm = null;
                return(false);
            }
            if (HashAlgorithmHelper.TryGetAlgorithm(prefix, out IHashAlgorithm hashAlgorithm))
            {
                switch (suffix)
                {
                case "CVC-ECDSA": algorithm = new CVC_ECDSA(hashAlgorithm); return(true);

                case "DSA": algorithm = new DSA(hashAlgorithm); return(true);

                case "ECDSA": algorithm = new ECDSA(hashAlgorithm); return(true);

                case "ECGOST3410":
                case "ECGOST3410-2001":
                case "ECGOST-3410":
                case "ECGOST-3410-2001": algorithm = new ECGOST3410(hashAlgorithm); return(true);

                case "ECNR": algorithm = new ECNR(hashAlgorithm); return(true);

                case "GOST3410":
                case "GOST3410-94":
                case "GOST-3410":
                case "GOST-3410-94": algorithm = new GOST3410(hashAlgorithm); return(true);

                case "PLAIN-ECDSA": algorithm = new PLAIN_ECDSA(hashAlgorithm); return(true);

                case "RSA": algorithm = new RSA(hashAlgorithm); return(true);

                case "ISO9796-2":
                case "RSA/ISO9796-2":
                case "RSAANDISO9796-2": algorithm = new RSAandISO9796_2(hashAlgorithm); return(true);

                case "RSAANDMGF1": algorithm = new RSAandMGF1(hashAlgorithm); return(true);

                case "RSA/X9.31":
                case "RSA/X931":
                case "RSAANDX931":
                case "RSAANDX9.31": algorithm = new RSAandX931(hashAlgorithm); return(true);

                case "SM2": algorithm = new SM2(hashAlgorithm); return(true);

                default: break;
                }
            }
            algorithm = null;
            return(false);
        }