You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
456 lines
18 KiB
456 lines
18 KiB
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;
|
|
}
|
|
}
|
|
}
|
|
|