NetCore 生成RSA公私鑰對,公鑰加密私鑰解密,私鑰加密公鑰解密
阿新 • • 發佈:2019-01-04
using Newtonsoft.Json; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; using System; using System.IO; using System.Text; namespace ConsoleAppTest { class Program { //自己生成 static string _publicKeyString = @"-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmu5B+WIU9ZK+uy11qmdj KygwcH6r8YOui2jc87pWShKAEnEE2Q81twgQ4KGFylodpNsXs+EGeQA8HUiDKK11 Pm6AMzrqHQ8rtNNWbQA6Ca+BJJHEoyrizaHTGLWU7oHe9TcRQ8Iq5vihF74x1jD2 BHXpgR6KUhx9DUMFgdwpLXhZ2EgG85eOuajcHl+tLN52bEpGwSHyL5iWuX2U5qfN Ln3h/lCmdktPwCjVVyaoLALxP0oDtalRwLKRJuD91p6YxrAeN3eRkGWoYXH5bSrS BR+5id5yBplxayVrozJXFpOFySTrCHD2snnfjmn39dKsRjwRjKG/zKUPO54GPovv rODK32c1xiKpQQ7XAzyXPNij3MWPJFu6z24eNdILm4wF1jFWQlrsca0znpjoAVFY e1yp1lXAtYlxgNURMQ2RwvBtnT3BUhUP382GYd0XPdV1xodOaJVf8ce3YImrGaAY jPjbTPTbTyzwcHyc1LHwYw4W7aYiTyX8p5+7PkdkRr8uiKA9iFhH6iq+7MPv5O0F NGSktGZkyFSKg9vazQmESlUC+blC2yQRBJsqV84weMKGCURg3jxetTfENHJ8N+H9 pnacvRjl5O5cZjECOi4XrGAUd4bvqKBfZ1OnB1htWGEjPyGhKzmAG8vzf0GVtAK8 8qhuYM7FceTYJsqUnzFnEHUCAwEAAQ== -----END PUBLIC KEY-----"; static string _privateKeyString = @"-----BEGIN RSA PRIVATE KEY----- MIIJKAIBAAKCAgEAmu5B+WIU9ZK+uy11qmdjKygwcH6r8YOui2jc87pWShKAEnEE 2Q81twgQ4KGFylodpNsXs+EGeQA8HUiDKK11Pm6AMzrqHQ8rtNNWbQA6Ca+BJJHE oyrizaHTGLWU7oHe9TcRQ8Iq5vihF74x1jD2BHXpgR6KUhx9DUMFgdwpLXhZ2EgG 85eOuajcHl+tLN52bEpGwSHyL5iWuX2U5qfNLn3h/lCmdktPwCjVVyaoLALxP0oD talRwLKRJuD91p6YxrAeN3eRkGWoYXH5bSrSBR+5id5yBplxayVrozJXFpOFySTr CHD2snnfjmn39dKsRjwRjKG/zKUPO54GPovvrODK32c1xiKpQQ7XAzyXPNij3MWP JFu6z24eNdILm4wF1jFWQlrsca0znpjoAVFYe1yp1lXAtYlxgNURMQ2RwvBtnT3B UhUP382GYd0XPdV1xodOaJVf8ce3YImrGaAYjPjbTPTbTyzwcHyc1LHwYw4W7aYi TyX8p5+7PkdkRr8uiKA9iFhH6iq+7MPv5O0FNGSktGZkyFSKg9vazQmESlUC+blC 2yQRBJsqV84weMKGCURg3jxetTfENHJ8N+H9pnacvRjl5O5cZjECOi4XrGAUd4bv qKBfZ1OnB1htWGEjPyGhKzmAG8vzf0GVtAK88qhuYM7FceTYJsqUnzFnEHUCAwEA AQKCAgAJNMGmVjHtIrZjLHEaulB9+sIgGSLR89NuFMHWWLryZVWHYwBY/oqgUOIh 2u82hjLPPjaTGXq3O3CXA91JUD9QgaPsB2LsbZ+Y+9rdmL8aHrvjhqbi1DRMaEKB LB8Iqh5rv0UvG81NUrZN0R8ofVnNC42fvscU2H0Eyff8jq7CtS1PiP4iwN16AkLs Kh7B9Qkf56z7Im9/7Qb9yCzOJOfwDIpegH/XtSNsxXBh4JnrHZSkwFrmk0RYmTO/ Vey3cU6eNc5h2ODFLgk6mxlpZ3s50zgE7Xskkb8X0Q4rkRz0KjlvqZousvmxwsSq M3lIuedDOCQJ0X2yZRd54Sq5UjrgWKWc7LT9qeiUYmdx3LM6LbG+DzGLHgQ7SkpT ZmOKmXOWtjQ/4LOF1vDS3vK7aGU6QjebswKCZv/b9hDiA2AWtoVM3rOlPc1ypif+ Uoq1PLxnjxIfptdwlZf9n9ol6+5K2E2PeV9d3nJ+idmoi7EnGNUB/kgud3Rv5uZ7 Ftf/cqHgUiwYuMR/n40xR4tx6w8SSIL31c83OZQbOsh/7fN89FWxqYeDzAir4Yj7 ozAUteyAnBn70BO0SzsNnt6jGwDcnyQuQtAnnoNgbEVzD6AOaTvmuKsx/RvDp+nv OoeGVRSZusJYL/DexH8Rp2js+ai3wI7hnf4YBgG/jQTf3L9egQKCAQEA7Vu1mWOp Nq96dCSIRBbu/3OhBhFDKuatxB1ZKoccADCaz4NqaWCUZr2PutSgDqc8qqIldnwr 0Cxojj658MJmMehECOsDiEBRlCsZX3ghstVzdbnuhGEsC4RBaaIY9Ik0Jj0b40Fa GDEAULk0cOucA3bPhyPiaGuo9dHQbREIGKqmGMxgSSm6CeuQcon253U5jNfrY3se LlLcBYKAAJMLW9c8niTZPXZA62nyGVH4aaLiyB+NodRgSO/zNwR2vMTxdJbCBQJ7 YJhiuGqYGSLnIf7MUBg5X0jL0oShNGngcCtLSwXwP3I0+AnMaKQo+93+gILy4J2Q yzkK0guR7E6x9QKCAQEApxlFn3smcad8SeI0fXd8nBLhk4GbNXCy+fMTy11zXmVJ M2HeE6u+nGLoaMrdL0IFyVOTDV6Cn5k1iPLFMTcGdLqrS+j/ICZh0+F0qg7x1inp O3Fsgp4C0eebcigk9//GCkFAvDfgfAPB3G6R6qA+xIq7P6M/D2Iy4wNQaeyK+ubC AIUydF9sdLgn/cwwBbo6E9GPr6LHy1AbNvK8/WkogKnXONocjejiE6Y45RcK7Ci/ iG1MlvTeXMT7A0ccKrJDxipfhpLfENS7TClvOoi6T5mGj4tAGaU1YqBC6FLB8Fk1 NR3BsByc/xShzmdelRlE5ZkD76dkt58KK9Dcpg9UgQKCAQBWFOSPdK1xv8JRTM5+ ozKaQn0rjicl7/MlJN+ppMZq6zW5/JcjEHvBpw3xXBuQNOazm4qqpe3d2b0xmV4m Ctq1Tt8dvN3TFECB0/+nuBHAodj14TC3oeYvZw+yF6+lP/1BwPfUO2dJTrwmioSS T2CNgRFSmZkK9cCZJjbI11xwVbNXWwewgQmPf3crkw2Qc9sjUUt1ELmpYzOYa/Ow QaPk8vJxCDwhJjn1Jr8+n5PP+9rzaT4casFgsmUqltvZKjPHbMWAu52zMpDbAQ2Y UCgzFA1VHvPvsp75AlDUiXF0PL9ZvhWbbPf43NTdPmv+rEwYn2UmQP66NT3PX5LU eNXhAoIBAQCfb9miphq0bx6EULXM0WmwccOSvyOdBf9/xpOog25FL1d9E3CWTFT/ WtBuzMHnCZQD2R6Crx7rsYAHP70vjFU4o7Q2JgboYjcNuB1c9g2CT1MfSMZEiwgd yGPltLAxgXcw6boe3SHwspUTAt1vhZ7eYZDw046j2wq62Lqtlum1JOEPJghSSYpu hBiEbYOUs3Ae4OG8sBD6yWBuOYZiNShUO7oDoPmF3iN4pC29SRM+6lJ2lfNyRgq1 ca4gHoWdp6JLPrsO6Ky2noRn01EyBKvgr5hY9oEe9yfNByVQD0bFUhkWqWHZ7DNQ oBsTmkYA9a9rc2jyDkJngfRugkdGm3IBAoIBAHrY2yYT59hFXq99HvH2MB5QcMlv TtOqjs1tmE3Mpivyg9YNvDeDTv1qOZn8xUd959nJrHSqN8me86P12DTQPCzdhIRT MM/bNjr6/bHhKG3nF/Xkln4hK5YtSH1PjCml+VEoUPFwgsWvn2dhYwsQ1wiarwkL FzIijlLmfuLGvS8kTPMRXert4UWZcXRD8B/MypYt3uMjLbeXIAHuOG9wjuuV/Yla XJBgOImw0Jhib7NqS0BKv3SvhHA7olduqOv9z0yGHXcHZKxB6O7+YKATc1nszjwc cAEm30c3/mhCHPuathM0laKV36Z4hp2y8ginJdK7gZQSkyzjRDI2yx5cq6U= -----END RSA PRIVATE KEY-----"; //網站生成 static string _publicKeyStringWeb = @"-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPNZe67AfIs2rFWLp5HZ6y8VG/ ybhhE0Q9WmkdnPXFGSN9j3M8Uglmh78MCfawm9goHnvMl5c8ta2dDnKZi+WWuZ6D K+OOS/kBgR9cLMci+9bI7NA8/spxbVBbKDlgyRWPle65Nm7foX3OamNJe6uoMaLL Y/jWSdwxPsp1jp8GJwIDAQAB -----END PUBLIC KEY-----"; static string _privateKeyStringWeb = @"-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,EF30E5A772A723B5 8DS2obJNGC9M2GJuxID6mZV5p4k+oxgv1fEILJ+CwpzJ0lAGspDiqLgxSKmETZiE ct9zCkEEABTarhBspC1i4O4np4nO+z40iTvDwu88bDMvWvEV2EYYaJn9ZelJZ51y KHEJw4fncvZN8ULNBZ7Fnn9Y6+pLW6IJrSHTL7TrN1gi257LAX5xUgHvNqet4XMl UKoX0DmDZkCFdnn3a/DQUA4au8rrotsU/ihBDFMMGljA/QKPApnuy07+KyXtaDx6 YlBMifqbsuYLOvNMT9aqbAPp5OFdykTnfPRPnx9xHuTqXE8NfInYvSMkcC8lpUrt M8wsCVaRukliBOSyNYxyqMZwBJMXrY9+lyZMi6/g6450HJ0NBOC8/mc5Gj8nD6L6 tjkkFyS/t4aU7lZWqh4oMRHPwDQqrgSZB13lBveXVSoTpVOzvgV2vEC6p9s+IGIC +WpNRq55zWtj8EsZly/p91NlrHujDQsEyrkUDUE3xjon66vQow9jRwYngA0VWeL8 dO9XN/srJ4jQB4I/wx9X7MfDJzVF/RrHT42QVfeqsPx071TJAd3+GqJS0+B1FaS8 PoTCY4yor2ViTOS/NfZEs0SyC1U9t41FTIEnfnZVqEF54Hy3l8kCrTjVc2Vm2HAG /DAzGgbdOV8KJKdWFpc5+aQde5j2xxd4LwF8kKTSLaRonVMhBJ4LBLPn9NsdU7vw MyFyLnHb4qsilAC3P2Ui8MJP2kJsqO1ho3V9ITG8IzxiZmpLLL1D8GAJoaDtDxhu MtQZQpgpuW7CaVItZrxrmBbdMXpGW/GkOhySZVI0au04GfPHQtssAQ== -----END RSA PRIVATE KEY-----"; static string _encryptedWithPublicStr = @"N+sAvcN+AveMeY1oehXHNMj4PAlLhNRSXgUrcTXLlOlezFyM2UwvSEXNchVs0r5aMYgn2gAX1ofOkXGzywvoaju2qDjcdZIV4WYlhR6RI0PEueootTRnQoJCNKnOaCQ2YZsXys5crm6xRMsFM7QcKKxjUWeg41Q8u5pjZOM3PtdoF+AZwweieNEJrWSsvGAKXfDol9R5fCJ2s51G5/6q+3U9wR8kWcZl7cpmdSJUWg2GSHcKVda6cJ2Ojr44wDLfiBTUz72dzDlZHJowHQc3s8+GYx6rIYQyghxYdiHfDPtQdOtPxdKaJajk+bJOyuEicYuW0j7CwV/+zD7XPoDkoNMbp6oikCBmU1d+zmWsfg4lySwPe++2irjJmCLaOeRHzvXdkfnUhWCzjfxPGSEE+feSMEvDbTAvXbhvtC7zguJBpy2eyQM+n0wtkXrsYOxBldVOT3M1CAKHEdBecoV7xxLPlrpnxpgf2jsWMdfRwXYVpaXacVQuAwe8MLCexlLEPusAAFMnp3DpBO4gbCQCRoAKJTN7MyJfKmeOLAKHR6M+NuO5+AZ2Z2sj06u9SNcYuKm5Jgl3NIDzTrVfNbFjaDqFLzWpHiv1JS20D21gUocycDAVLw09U3aAz5KzBpbPQ2knqslgnBi1116xKzCOEhqB6mMNuoVPrDUr2eIFuns="; static string _encryptedWithPrivateStr = @"dkOvosyTPPOrAglholTHEqu/RoyQv1Ih7qy/SsuxjoO92ZpL4alNhso6snPyooZmOBP/ljb91BEXaaqqaLneYzvlvEAqJPKXyOvYFREhcwLXCE9dIbn0NPs7srRETd0JqeQFyRv6qGvoA2aXg4Fxch7cANqpKY03eb3htuqTTranAZsPT3SYZPKOxK4wgfU/63wDBCWEO0SeNga86QgLpfcQ0DRukqNVr64lDt2QddUqeKxbnAJM2u2xpwnGayFDKP03BjV6rXnId7NlkgkoqZw3SpyOg5HOe/4tTbRQ4gjH6N5Q4VnyFGehOw3SH/KNWT/KaE2anI9vWCZHy0ia8mxiBKH0vuJl3WEoR1JPFmhSrbcemrWpgc0a8r6CJQRMefYw18z3AuHRu3vAOPEtS9bXaySpQ0PLjS/pxG3Y1MTZLQPBvCcY77Ghr35UNJ2Z8XIbiwt9dqs0Az7y6ARj2qyz+96ZQkstrDJw4OikZj0w6olHoL9WQ2b04S7wz6GE87/5AS3hphZJNwFpvHxaUWTRYf+N2r9GrjM3nplYqBAQwz1XI65qhQL4PBK7/4HTgkVBDNRsJNHOJp5Uq4+UtZYwgwLOjxhBG5dlLJMCbZvABqE8e4gRLDv8wfBiC8qS4CxhOz3HuxW+KdZFkEG9Qajf/hxEzwUoxf+mLagdp1U="; static string _encryptedWithPublicWebStr = @"KRC9OJ3R981IsvBJ33Z9eDJHgg5+EOcnh95yZA1HDPeOnn7+s7n7TH4yyIOVAS677bESftzcNfhyCCrDVb7GkRBTu3UgMlkl/KlkC4a8yS2skks5TwYkjPmr2I31PCZzg9JuH0eo3S4oXzqg9NVhgW37kUgRjjbH489XFYeUQEM="; static string _encryptedWithPrivateWebStr = @"i8lGOft4QH781r5+GUuzp3zEjmmpyw3N7qjj6wKH6LRjxzzCwxJqyewztmosrO9aJt/0LEew5wMSt/xWjrHNiaiAdxnI0UX7i7TZWNASXe1Sie/+36egYfu5x4BctIh2NPP3c6HixVJzQ2I5vtuKK2UPEw8yu5kH3p7+Ifp7CbU="; static void Main(string[] args) { Console.WriteLine("Hello World!"); //var jsonStr = JsonConvert.SerializeObject(new Test { Name = "塞爾達荒野之息" }); //var jsonByte = Convert.ToByte(jsonStr); //Console.WriteLine(jsonStr); //GenRSAKeyPair("xjl"); //RsaEncryptWithPublic("yahaha"); //RsaEncryptWithPrivate("link"); string encryptedTxt = RsaEncryptWithPublic(new Test { Name = "噴射戰士2haha" }); //RsaDecryptWithPrivate(_encryptedWithPublicStr); RsaDecryptWithPrivate(encryptedTxt); //RsaEncryptWithPrivate(new Test { Name = "odessey" }); //RsaDecryptWithPublic(_encryptedWithPrivateStr); Console.ReadLine(); } public static void GenRSAKeyPair(string name) { var generator = new RsaKeyPairGenerator(); var seed = Encoding.UTF8.GetBytes(name); var secureRandom = new SecureRandom(); secureRandom.SetSeed(seed); generator.Init(new KeyGenerationParameters(secureRandom, 4096)); var pair = generator.GenerateKeyPair(); //第一種方案 //var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private); //var serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded(); //var serializedPrivate = Convert.ToBase64String(serializedPrivateBytes); //Console.WriteLine("Private Key:" + serializedPrivate); //var publickKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pair.Public); //var serializedPublicBytes = publickKeyInfo.ToAsn1Object().GetDerEncoded(); //var serializedPublic = Convert.ToBase64String(serializedPublicBytes); //Console.WriteLine("Public Key:" + serializedPublic); //第二種方案 var twPrivate = new StringWriter(); PemWriter pwPrivate = new PemWriter(twPrivate); pwPrivate.WriteObject(pair.Private); pwPrivate.Writer.Flush(); var privateKey = twPrivate.ToString(); Console.WriteLine("Private Key:" + privateKey); var twPublic = new StringWriter(); PemWriter pwPublic = new PemWriter(twPublic); pwPublic.WriteObject(pair.Public); pwPublic.Writer.Flush(); var publicKey = twPublic.ToString(); Console.WriteLine("Public Key:" + publicKey); } /// <summary> /// 公鑰加密 /// </summary> /// <param name="dataToEncrypt">待加密文字</param> //public static void RsaEncryptWithPublic(string dataToEncrypt) public static string RsaEncryptWithPublic(Test dataToEncrypt) { var bytesToEncrypt = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dataToEncrypt)); var encryptEngine = new Pkcs1Encoding(new RsaEngine()); using (var txtReader = new StringReader(_publicKeyString)) { var keyParameter = (AsymmetricKeyParameter)new PemReader(txtReader).ReadObject(); encryptEngine.Init(true, keyParameter); } var encryptedText = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length)); // 獲取publickKey對應的keyParameter的第一種方法 //var stringReader = new StringReader(_publicKeyString); //var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(stringReader); //var keyParameter = (AsymmetricKeyParameter)pemReader.ReadObject(); // 獲取publickKey對應的keyparameter的第二種方法 //var publicKey = Convert.FromBase64String(_publicKeyString); //var keyParameter = PublicKeyFactory.CreateKey(publicKey); //用公鑰加密的第一種方法 //var engine = new RsaEngine(); //engine.Init(true, keyParamter); //var blockSize = engine.GetInputBlockSize(); //return engine.ProcessBlock(data, 0, blockSize); //用公鑰加密的第二種方法 //IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); //cipher.Init(true, keyParameter); //var encryptedText = Convert.ToBase64String(cipher.DoFinal(bytesToEncrypt)); Console.WriteLine("利用公鑰加密後的資料:" + encryptedText); return encryptedText; } /// <summary> /// 私鑰加密 /// </summary> /// <param name="dataToEncrypt">待加密文字</param> //public static void RsaEncryptWithPrivate(string dataToEncrypt) public static void RsaEncryptWithPrivate(Test dataToEncrypt) { var bytesToEncrypt = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dataToEncrypt)); var encryptEngine = new Pkcs1Encoding(new RsaEngine()); using (var txtReader = new StringReader(_privateKeyString)) { var keyPair = (AsymmetricCipherKeyPair)new PemReader(txtReader,new PasswordFinder("xjl")).ReadObject(); encryptEngine.Init(true, keyPair.Private); } var encryptedText = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length)); Console.WriteLine("利用私鑰加密後的資料:" + encryptedText); } public class Test { public string Name { get; set; } } /// <summary> /// 私鑰解密 /// </summary> /// <param name="dataToDecrypt">待解密的資料</param> public static void RsaDecryptWithPrivate(string dataToDecrypt) { var bytesToDecrypt = Convert.FromBase64String(dataToDecrypt); // 獲取privatekey對應的keyparameter的第一種方法 var stringReader = new StringReader(_privateKeyString); //var pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader); var pemReader = new PemReader(stringReader, new PasswordFinder("xjl")); var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); var keyParameter = keyPair.Private; // 獲取privatekey對應的keyparameter的第二種方法 //var publicKey = Convert.FromBase64String(_publicKeyString); //var keyParameter = PublicKeyFactory.CreateKey(publicKey); // 用私鑰解密的第一種方法 //var engine = new RsaEngine(); //engine.Init(false, keyParameter); //var blockSize = engine.GetInputBlockSize(); //engine.ProcessBlock(dataToDecrypt, 0, blockSize); // 用私鑰解密的第二種方法 IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); cipher.Init(false, keyParameter); var decipheredBytes = cipher.DoFinal(bytesToDecrypt); var decipheredText = Encoding.UTF8.GetString(decipheredBytes); Console.WriteLine("用私鑰解密後的資料:" + decipheredText); } /// <summary> /// 公鑰解密 /// </summary> /// <param name="dataToDecrypt">待解密的資料</param> public static void RsaDecryptWithPublic(string dataToDecrypt) { var bytesToDecrypt = Convert.FromBase64String(dataToDecrypt); // 獲取privatekey對應的keyparameter的第一種方法 var stringReader = new StringReader(_publicKeyString); //var pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader); var pemReader = new PemReader(stringReader); var keyParameter = (AsymmetricKeyParameter)pemReader.ReadObject(); // 獲取privatekey對應的keyparameter的第二種方法 //var publicKey = Convert.FromBase64String(_publicKeyString); //var keyParameter = PublicKeyFactory.CreateKey(publicKey); // 用私鑰解密的第一種方法 //var engine = new RsaEngine(); //engine.Init(false, keyParameter); //var blockSize = engine.GetInputBlockSize(); //engine.ProcessBlock(dataToDecrypt, 0, blockSize); // 用私鑰解密的第二種方法 IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); cipher.Init(false, keyParameter); var decipheredBytes = cipher.DoFinal(bytesToDecrypt); var decipheredText = Encoding.UTF8.GetString(decipheredBytes); Console.WriteLine("用公鑰解密後的資料:" + decipheredText); } } }