using Sog; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security; using System.Text; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; namespace HttpProxyPay { public static class SHARSASign { private static string ALGORITHM = "AES/GCM/NoPadding"; private static int TAG_LENGTH_BIT = 128; private static int NONCE_LENGTH_BYTE = 12; public static string AesGcmDecrypt(string AES_KEY, string associatedData, string nonce, string ciphertext) { GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine()); AeadParameters aeadParameters = new AeadParameters( new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)), 128, Encoding.UTF8.GetBytes(nonce), Encoding.UTF8.GetBytes(associatedData)); gcmBlockCipher.Init(false, aeadParameters); byte[] data = Convert.FromBase64String(ciphertext); byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)]; int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0); gcmBlockCipher.DoFinal(plaintext, length); return Encoding.UTF8.GetString(plaintext); } public static byte[] StrToHexByte(string hexString) { hexString = hexString.Replace(" ", ""); if ((hexString.Length % 2) != 0) hexString += " "; byte[] returnBytes = new byte[hexString.Length / 2]; for (int i = 0; i < returnBytes.Length; i++) returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); return returnBytes; } //合并字节 public static byte[] Concat(byte[] a, byte[] b) { byte[] output = new byte[a.Length + b.Length]; for (int i = 0; i < a.Length; i++) { output[i] = a[i]; } for (int j = 0; j < b.Length; j++) { output[a.Length + j] = b[j]; } return output; } //去掉前面12个字符 public static byte[] Sub(int subNum, byte[] b) { byte[] output = new byte[b.Length - subNum]; for (int i = 0; i < b.Length; i++) { if (i < subNum) { continue; } output[i - 12] = b[i]; } return output; } public static byte[] StringToByteArray(string hex) { return Enumerable.Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); } public static bool VerifyWithSHA256RSA(string text, string pubPem, string sign) { byte[] byteSign = Convert.FromBase64String(sign); byte[] byteText = Encoding.UTF8.GetBytes(text); //byte[] signatureAsBytes = StringToByteArray(sign); RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(); rsaCryptoServiceProvider.ImportFromPem(pubPem); //var hashData = SHA256.Create().ComputeHash(byteText); //var result2 = rsaCryptoServiceProvider.VerifyHash(hashData, CryptoConfig.MapNameToOID("SHA256"), byteSign); //var result3 = rsaCryptoServiceProvider.VerifyHash(hashData, byteSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); //TraceLog.Trace("VerifyWithSHA256RSA result2={0}", result2); //TraceLog.Trace("VerifyWithSHA256RSA result3={0}", result3); //var result4 = rsaCryptoServiceProvider.VerifyData(byteText, byteSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); //TraceLog.Trace("VerifyWithSHA256RSA result4={0}", result4); return rsaCryptoServiceProvider.VerifyData(byteText, CryptoConfig.MapNameToOID("SHA256"), byteSign); } public static string SignWithSHA256RSA(string text, string pem ) { byte[] byteText = Encoding.UTF8.GetBytes(text); RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(); rsaCryptoServiceProvider.ImportFromPem(pem); var byteRSA = rsaCryptoServiceProvider.SignData(byteText, CryptoConfig.MapNameToOID("SHA256")); string sign = Convert.ToBase64String(byteRSA); return sign; } public static string SignSha256RSA(string text, string keyPem) { // encoding my privateKey from string to byte[] by using DecodeOpenSSLPrivateKey function from OpenSSLKey source code byte[] pemprivatekey = DecodeOpenSSLPrivateKey(keyPem); // enconding my string to sign in byte[] byte[] byteSign = Encoding.ASCII.GetBytes(text); // using DecodeRSAPrivateKey function from OpenSSLKey source code to get the RSACryptoServiceProvider with all needed parameters var rsa = DecodeRSAPrivateKey(pemprivatekey); // Signing my string with previously get RSACryptoServiceProvider in SHA256 var byteRSA = rsa.SignData(byteSign, CryptoConfig.MapNameToOID("SHA256")); // As required by docs converting the signed string to base64 string sign = Convert.ToBase64String(byteRSA); return sign; } public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key ------ MemoryStream mem = new MemoryStream(privkey); BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; int elems = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; twobytes = binr.ReadUInt16(); if (twobytes != 0x0102) //version number return null; bt = binr.ReadByte(); if (bt != 0x00) return null; //------ all private key components are Integer sequences ---- elems = GetIntegerSize(binr); MODULUS = binr.ReadBytes(elems); elems = GetIntegerSize(binr); E = binr.ReadBytes(elems); elems = GetIntegerSize(binr); D = binr.ReadBytes(elems); elems = GetIntegerSize(binr); P = binr.ReadBytes(elems); elems = GetIntegerSize(binr); Q = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DP = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DQ = binr.ReadBytes(elems); elems = GetIntegerSize(binr); IQ = binr.ReadBytes(elems); // ------- create RSACryptoServiceProvider instance and initialize with public key ----- RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); RSAParameters RSAparams = new RSAParameters(); RSAparams.Modulus = MODULUS; RSAparams.Exponent = E; RSAparams.D = D; RSAparams.P = P; RSAparams.Q = Q; RSAparams.DP = DP; RSAparams.DQ = DQ; RSAparams.InverseQ = IQ; RSA.ImportParameters(RSAparams); return RSA; } catch (Exception) { return null; } finally { binr.Close(); } } public static byte[] DecodeOpenSSLPrivateKey(String instr) { //const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----"; //const String pemprivfooter = "-----END RSA PRIVATE KEY-----"; String pemstr = instr.Trim(); byte[] binkey; //if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) // return null; StringBuilder sb = new StringBuilder(pemstr); //sb.Replace(pemprivheader, ""); //remove headers/footers, if present //sb.Replace(pemprivfooter, ""); String pvkstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace try { // if there are no PEM encryption info lines, this is an UNencrypted PEM private key binkey = Convert.FromBase64String(pvkstr); return binkey; } catch (System.FormatException) { //if can't b64 decode, it must be an encrypted private key //Console.WriteLine("Not an unencrypted OpenSSL PEM private key"); } StringReader str = new StringReader(pvkstr); //-------- read PEM encryption info. lines and extract salt ----- if (!str.ReadLine().StartsWith("Proc-Type: 4,ENCRYPTED")) return null; String saltline = str.ReadLine(); if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,")) return null; String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim(); byte[] salt = new byte[saltstr.Length / 2]; for (int i = 0; i < salt.Length; i++) salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16); if (!(str.ReadLine() == "")) return null; //------ remaining b64 data is encrypted RSA key ---- String encryptedstr = str.ReadToEnd(); try { //should have b64 encrypted RSA key now binkey = Convert.FromBase64String(encryptedstr); } catch (System.FormatException) { // bad b64 data. return null; } //------ Get the 3DES 24 byte key using PDK used by OpenSSL ---- SecureString despswd = GetSecPswd("Enter password to derive 3DES key==>"); //Console.Write("\nEnter password to derive 3DES key: "); //String pswd = Console.ReadLine(); byte[] deskey = GetOpenSSL3deskey(salt, despswd, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes if (deskey == null) return null; //showBytes("3DES key", deskey) ; //------ Decrypt the encrypted 3des-encrypted RSA private key ------ byte[] rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV if (rsakey != null) return rsakey; //we have a decrypted RSA private key else { Console.WriteLine("Failed to decrypt RSA private key; probably wrong password."); return null; } } private static byte[] GetOpenSSL3deskey(byte[] salt, SecureString secpswd, int count, int miter) { IntPtr unmanagedPswd = IntPtr.Zero; int HASHLENGTH = 16; //MD5 bytes byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store contatenated Mi hashed results byte[] psbytes = new byte[secpswd.Length]; unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd); Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length); Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd); //UTF8Encoding utf8 = new UTF8Encoding(); //byte[] psbytes = utf8.GetBytes(pswd); // --- contatenate salt and pswd bytes into fixed data array --- byte[] data00 = new byte[psbytes.Length + salt.Length]; Array.Copy(psbytes, data00, psbytes.Length); //copy the pswd bytes Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = null; byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget for (int j = 0; j < miter; j++) { // ---- Now hash consecutively for count times ------ if (j == 0) result = data00; //initialize else { Array.Copy(result, hashtarget, result.Length); Array.Copy(data00, 0, hashtarget, result.Length, data00.Length); result = hashtarget; //Console.WriteLine("Updated new initial hash target:") ; //showBytes(result) ; } for (int i = 0; i < count; i++) result = md5.ComputeHash(result); Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial } //showBytes("Final key material", keymaterial); byte[] deskey = new byte[24]; Array.Copy(keymaterial, deskey, deskey.Length); Array.Clear(psbytes, 0, psbytes.Length); Array.Clear(data00, 0, data00.Length); Array.Clear(result, 0, result.Length); Array.Clear(hashtarget, 0, hashtarget.Length); Array.Clear(keymaterial, 0, keymaterial.Length); return deskey; } public static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) { MemoryStream memst = new MemoryStream(); TripleDES alg = TripleDES.Create(); alg.Key = desKey; alg.IV = IV; try { CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); cs.Write(cipherData, 0, cipherData.Length); cs.Close(); } catch (Exception exc) { Console.WriteLine(exc.Message); return null; } byte[] decryptedData = memst.ToArray(); return decryptedData; } private static SecureString GetSecPswd(String prompt) { SecureString password = new SecureString(); Console.ForegroundColor = ConsoleColor.Gray; Console.Write(prompt); Console.ForegroundColor = ConsoleColor.Magenta; while (true) { ConsoleKeyInfo cki = Console.ReadKey(true); if (cki.Key == ConsoleKey.Enter) { Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(); return password; } else if (cki.Key == ConsoleKey.Backspace) { // remove the last asterisk from the screen... if (password.Length > 0) { Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); Console.Write(" "); Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); password.RemoveAt(password.Length - 1); } } else if (cki.Key == ConsoleKey.Escape) { Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(); return password; } else if (Char.IsLetterOrDigit(cki.KeyChar) || Char.IsSymbol(cki.KeyChar)) { if (password.Length < 20) { password.AppendChar(cki.KeyChar); Console.Write("*"); } else { Console.Beep(); } } else { Console.Beep(); } } } private static int GetIntegerSize(BinaryReader binr) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binr.ReadByte(); if (bt != 0x02) //expect integer return 0; bt = binr.ReadByte(); if (bt == 0x81) count = binr.ReadByte(); // data size in next byte else if (bt == 0x82) { highbyte = binr.ReadByte(); // data size in next 2 bytes lowbyte = binr.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else { count = bt; // we already have the data size } while (binr.ReadByte() == 0x00) { //remove high order zeros in data count -= 1; } binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte return count; } } }