using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 加密文本类型
/// </summary>
public enum CryptoTextType
{
Invalid = -1,
Hex,
Base64,
Base62,
Base62V2,
Count,
}
public enum DesEdeKeyLen
{
KL112 = 112,
KL168 = 168,
KL192 = 192
}
/// <summary>
/// 3DES 加密解密
/// </summary>
public class BouncyCastleDesEdeUtils
{
/// <summary>
/// key 合法长度 DesEdeKeyLen
/// </summary>
/// <param name="KELen"></param>
/// <returns></returns>
public static bool GenerationKey(ref String key,DesEdeKeyLen KELen = DesEdeKeyLen.KL192,
CryptoTextType cType = CryptoTextType.Base64)
{
SecureRandom sr = new SecureRandom();
KeyGenerationParameters kgp = new KeyGenerationParameters(
sr,(int)KELen);
DesEdeKeyGenerator kg = new DesEdeKeyGenerator();
kg.Init(kgp);
byte[] bskey = kg.GenerateKey();
bool ret = DesEdeParameters.IsRealEdeKey(bskey, 0);
key = String.Empty;
switch (cType)
{
case CryptoTextType.Hex:
key = Hex.ToHexString(bskey);
break;
case CryptoTextType.Base64:
key = Convert.ToBase64String(bskey);
break;
case CryptoTextType.Base62:
key = EncodingExtensions.ToBase62(bskey);
break;
case CryptoTextType.Base62V2:
key = EncodingExtensions.ToBase62V2(bskey);
break;
default:
key = Convert.ToBase64String(bskey);
break;
}
return ret;
}
public static String EncryptByKey(String key, String data,
CryptoTextType keyType = CryptoTextType.Base64,
CryptoTextType dataType = CryptoTextType.Base64
)
{
byte[] bsKey = null;
switch (keyType)
{
case CryptoTextType.Hex:
bsKey = Hex.Decode(key);
break;
case CryptoTextType.Base64:
bsKey = Convert.FromBase64String(key);
break;
case CryptoTextType.Base62:
bsKey = EncodingExtensions.FromBase62(key);
break;
case CryptoTextType.Base62V2:
bsKey = EncodingExtensions.FromBase62V2(key);
break;
default:
bsKey = Convert.FromBase64String(key);
break;
}
byte[] bsData = null;
switch (dataType)
{
case CryptoTextType.Hex:
bsData = Hex.Decode(data);
break;
case CryptoTextType.Base64:
bsData = Convert.FromBase64String(data);
break;
case CryptoTextType.Base62:
bsData = EncodingExtensions.FromBase62(data);
break;
case CryptoTextType.Base62V2:
bsData = EncodingExtensions.FromBase62V2(data);
break;
default:
bsData = Convert.FromBase64String(data);
break;
}
string encode = EncryptByKey(bsKey, bsData, dataType);
return encode;
}
public static String EncryptByKey(byte[] hex_key, byte[] hex_data,
CryptoTextType cType = CryptoTextType.Base64)
{
try
{
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CbcBlockCipher(new DesEdeEngine()));
cipher.Init(true, new DesEdeParameters(hex_key));
byte[] encryptBytes = cipher.DoFinal(hex_data, 0, hex_data.Length);
string encode = string.Empty;
switch (cType)
{
case CryptoTextType.Hex:
encode = BitConverter.ToString(encryptBytes).Replace("-", "");
break;
case CryptoTextType.Base64:
encode = Convert.ToBase64String(encryptBytes);
break;
case CryptoTextType.Base62:
encode = EncodingExtensions.ToBase62(encryptBytes);
break;
case CryptoTextType.Base62V2:
encode = EncodingExtensions.ToBase62V2(encryptBytes);
break;
default:
encode = Convert.ToBase64String(encryptBytes);
break;
}
return encode;
}
catch(Exception)
{
}
return "";
}
public static String DecryptByKey(byte[] hex_key, byte[] hex_data,
CryptoTextType cType = CryptoTextType.Base64)
{
try
{
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CbcBlockCipher(new DesEdeEngine()));
cipher.Init(false, new DesEdeParameters(hex_key));
byte[] encryptBytes = cipher.DoFinal(hex_data, 0, hex_data.Length);
string encode = string.Empty;
switch (cType)
{
case CryptoTextType.Hex:
encode = BitConverter.ToString(encryptBytes).Replace("-", "");
break;
case CryptoTextType.Base64:
encode = Convert.ToBase64String(encryptBytes);
break;
case CryptoTextType.Base62:
encode = EncodingExtensions.ToBase62(encryptBytes);
break;
case CryptoTextType.Base62V2:
encode = EncodingExtensions.ToBase62V2(encryptBytes);
break;
default:
encode = Convert.ToBase64String(encryptBytes);
break;
}
return encode;
}
catch (CryptoException)
{
}
return "";
}
public static String DecryptByKey(String key, String data,
CryptoTextType keyType = CryptoTextType.Base64,
CryptoTextType dataType = CryptoTextType.Base64)
{
byte[] bsKey = null;
switch (keyType)
{
case CryptoTextType.Hex:
bsKey = Hex.Decode(key);
break;
case CryptoTextType.Base64:
bsKey = Convert.FromBase64String(key);
break;
case CryptoTextType.Base62:
bsKey = EncodingExtensions.FromBase62(key);
break;
case CryptoTextType.Base62V2:
bsKey = EncodingExtensions.FromBase62V2(key);
break;
default:
bsKey = Convert.FromBase64String(key);
break;
}
byte[] bsData = null;
switch (dataType)
{
case CryptoTextType.Hex:
bsData = Hex.Decode(data);
break;
case CryptoTextType.Base64:
bsData = Convert.FromBase64String(data);
break;
case CryptoTextType.Base62:
bsData = EncodingExtensions.FromBase62(data);
break;
case CryptoTextType.Base62V2:
bsData = EncodingExtensions.FromBase62V2(data);
break;
default:
bsData = Convert.FromBase64String(data);
break;
}
return DecryptByKey(bsKey, bsData, dataType);
}
public static void Test()
{
string input = "4e6f77206973207468652074696d6520666f7220616c6c204e6f77206973207468652074696d6520666f7220616c6c20";
input = Convert.ToBase64String(Encoding.Default.GetBytes(input));
String key = "";
BouncyCastleDesEdeUtils.GenerationKey(ref key);
//String sKey = "ef0d9b7f704ccd5be591945713b51a375d92c7922552e0bc";
//key = Hex.Decode(sKey);
Console.WriteLine($"3des key={key}");
Console.WriteLine($"3des input={input}");
String en_data = BouncyCastleDesEdeUtils.EncryptByKey(key, input,dataType: CryptoTextType.Base64);
Console.WriteLine($"3des en={en_data}");
String de_data = BouncyCastleDesEdeUtils.DecryptByKey(key, en_data,dataType: CryptoTextType.Base64);
Console.WriteLine($"3des output:{de_data}");
bool ret = String.Equals(input, de_data, StringComparison.InvariantCultureIgnoreCase);
Console.WriteLine($"3des equal = {ret}");
}
}
public static class EncodingExtensions
{
private static string Base62CodingSpace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static char fillChar = '9'; // 填充符
/// <summary>
/// Convert a byte array
/// </summary>
/// <param name="original">Byte array</param>
/// <returns>Base62 string</returns>
public static string ToBase62(this byte[] original)
{
StringBuilder sb = new StringBuilder();
BitStream stream = new BitStream(original); // Set up the BitStream
byte[] read = new byte[1]; // Only read 6-bit at a time
while (true)
{
read[0] = 0;
int length = stream.Read(read, 0, 6); // Try to read 6 bits
if (length == 6) // Not reaching the end
{
if ((int)(read[0] >> 3) == 0x1f) // First 5-bit is 11111
{
sb.Append(Base62CodingSpace[61]);
stream.Seek(-1, SeekOrigin.Current); // Leave the 6th bit to next group
}
else if ((int)(read[0] >> 3) == 0x1e) // First 5-bit is 11110
{
sb.Append(Base62CodingSpace[60]);
stream.Seek(-1, SeekOrigin.Current);
}
else // Encode 6-bit
{
sb.Append(Base62CodingSpace[(int)(read[0] >> 2)]);
}
}
else if (length == 0) // Reached the end completely
{
break;
}
else // Reached the end with some bits left
{
// Padding 0s to make the last bits to 6 bit
sb.Append(Base62CodingSpace[(int)(read[0] >> (int)(8 - length))]);
break;
}
}
return sb.ToString();
}
/// <summary>
/// Convert a Base62 string to byte array
/// </summary>
/// <param name="base62">Base62 string</param>
/// <returns>Byte array</returns>
public static byte[] FromBase62(this string base62)
{
// Character count
int count = 0;
// Set up the BitStream
BitStream stream = new BitStream(base62.Length * 6 / 8);
foreach (char c in base62)
{
// Look up coding table
int index = Base62CodingSpace.IndexOf(c);
// If end is reached
if (count == base62.Length - 1)
{
// Check if the ending is good
int mod = (int)(stream.Position % 8);
if (mod == 0)
{
throw new InvalidDataException("an extra character was found");
}
// (8 - mod)剩余待写入位数大于6, 说明字节缺失
if ((8 - mod) > 6)
{
throw new InvalidDataException("ending character was lost");
}
// index位移操作之后大于零, 说明剩余bit空间不足
if ((index >> (8 - mod)) > 0)
{
throw new InvalidDataException("invalid ending character was found");
}
stream.Write(new byte[] { (byte)(index << mod) }, 0, 8 - mod);
}
else
{
// If 60 or 61 then only write 5 bits to the stream, otherwise 6 bits.
if (index == 60)
{
stream.Write(new byte[] { 0xf0 }, 0, 5);
}
else if (index == 61)
{
stream.Write(new byte[] { 0xf8 }, 0, 5);
}
else
{
stream.Write(new byte[] { (byte)index }, 2, 6);
}
}
count++;
}
// Dump out the bytes
byte[] result = new byte[stream.Position / 8];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(result, 0, result.Length * 8);
return result;
}
/// <summary>
/// 新版Base62加密, 4的倍数后面加一个填充符9
/// </summary>
/// <param name="original">Byte array</param>
/// <returns>Base62 string</returns>
public static string ToBase62V2(this byte[] original)
{
string encode = ToBase62(original);
return (encode.Length % 4 == 0) ? (encode + fillChar) : encode;
}
/// <summary>
/// 新版Base62解密, 4的倍数+1时, 尾部是A尝试解析两次
/// </summary>
/// <param name="base62">Base62 string</param>
/// <returns>Byte array</returns>
public static byte[] FromBase62V2(this string base62)
{
// 最后一位是A
if ((base62.Length % 4 == 1) && (base62[base62.Length - 1] == fillChar))
{
return FromBase62(base62.Substring(0, base62.Length - 1));
}
else
{
return FromBase62(base62);
}
}
/// <summary>
/// 解析Base62
/// </summary>
/// <param name="base62"></param>
/// <returns></returns>
public static byte[] DecodeBase62(this string base62)
{
// 1. 去掉填充符解析
byte[] result = new byte[0];
if (TryDecode(base62.Substring(0, base62.Length - 1), ref result))
{
return result;
}
// 2. 解析原字符串
if (TryDecode(base62, ref result))
{
return result;
}
return result;
}
public static bool TryDecode(this string base62, ref byte[] result)
{
// Character count
int count = 0;
// Set up the BitStream
BitStream stream = new BitStream(base62.Length * 6 / 8);
foreach (char c in base62)
{
// Look up coding table
int index = Base62CodingSpace.IndexOf(c);
// If end is reached
if (count == base62.Length - 1)
{
// Check if the ending is good
int mod = (int)(stream.Position % 8);
if (mod == 0)
{
// InvalidDataException("an extra character was found");
return false;
}
// (8 - mod)剩余待写入位数大于6, 说明字节缺失
if ((8 - mod) > 6)
{
// InvalidDataException("ending character was lost");
return false;
}
// index位移操作之后大于零, 说明剩余bit空间不足
if ((index >> (8 - mod)) > 0)
{
// InvalidDataException("invalid ending character was found");
return false;
}
stream.Write(new byte[] { (byte)(index << mod) }, 0, 8 - mod);
}
else
{
// If 60 or 61 then only write 5 bits to the stream, otherwise 6 bits.
if (index == 60)
{
stream.Write(new byte[] { 0xf0 }, 0, 5);
}
else if (index == 61)
{
stream.Write(new byte[] { 0xf8 }, 0, 5);
}
else
{
stream.Write(new byte[] { (byte)index }, 2, 6);
}
}
count++;
}
// Dump out the bytes
result = new byte[stream.Position / 8];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(result, 0, result.Length * 8);
return true;
}
}
/// <summary>
/// Utility that read and write bits in byte array
/// </summary>
public class BitStream : Stream
{
private byte[] Source { get; set; }
/// <summary>
/// Initialize the stream with capacity
/// </summary>
/// <param name="capacity">Capacity of the stream</param>
public BitStream(int capacity)
{
this.Source = new byte[capacity];
}
/// <summary>
/// Initialize the stream with a source byte array
/// </summary>
/// <param name="source"></param>
public BitStream(byte[] source)
{
this.Source = source;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
throw new NotImplementedException();
}
/// <summary>
/// Bit length of the stream
/// </summary>
public override long Length
{
get { return Source.Length * 8; }
}
/// <summary>
/// Bit position of the stream
/// </summary>
public override long Position { get; set; }
/// <summary>
/// Read the stream to the buffer
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="offset">Offset bit start position of the stream</param>
/// <param name="count">Number of bits to read</param>
/// <returns>Number of bits read</returns>
public override int Read(byte[] buffer, int offset, int count)
{
// Temporary position cursor
long tempPos = this.Position;
tempPos += offset;
// Buffer byte position and in-byte position
int readPosCount = 0, readPosMod = 0;
// Stream byte position and in-byte position
long posCount = tempPos >> 3;
int posMod = (int)(tempPos - ((tempPos >> 3) << 3));
while (tempPos < this.Position + offset + count && tempPos < this.Length)
{
// Copy the bit from the stream to buffer
if ((((int)this.Source[posCount]) & (0x1 << (7 - posMod))) != 0)
{
buffer[readPosCount] = (byte)((int)(buffer[readPosCount]) | (0x1 << (7 - readPosMod)));
}
else
{
buffer[readPosCount] = (byte)((int)(buffer[readPosCount]) & (0xffffffff - (0x1 << (7 - readPosMod))));
}
// Increment position cursors
tempPos++;
if (posMod == 7)
{
posMod = 0;
posCount++;
}
else
{
posMod++;
}
if (readPosMod == 7)
{
readPosMod = 0;
readPosCount++;
}
else
{
readPosMod++;
}
}
int bits = (int)(tempPos - this.Position - offset);
this.Position = tempPos;
return bits;
}
/// <summary>
/// Set up the stream position
/// </summary>
/// <param name="offset">Position</param>
/// <param name="origin">Position origin</param>
/// <returns>Position after setup</returns>
public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case (SeekOrigin.Begin):
{
this.Position = offset;
break;
}
case (SeekOrigin.Current):
{
this.Position += offset;
break;
}
case (SeekOrigin.End):
{
this.Position = this.Length + offset;
break;
}
}
return this.Position;
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
/// <summary>
/// Write from buffer to the stream
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset">Offset start bit position of buffer</param>
/// <param name="count">Number of bits</param>
public override void Write(byte[] buffer, int offset, int count)
{
// Temporary position cursor
long tempPos = this.Position;
// Buffer byte position and in-byte position
int readPosCount = offset >> 3, readPosMod = offset - ((offset >> 3) << 3);
// Stream byte position and in-byte position
long posCount = tempPos >> 3;
int posMod = (int)(tempPos - ((tempPos >> 3) << 3));
while (tempPos < this.Position + count && tempPos < this.Length)
{
// Copy the bit from buffer to the stream
if ((((int)buffer[readPosCount]) & (0x1 << (7 - readPosMod))) != 0)
{
this.Source[posCount] = (byte)((int)(this.Source[posCount]) | (0x1 << (7 - posMod)));
}
else
{
this.Source[posCount] = (byte)((int)(this.Source[posCount]) & (0xffffffff - (0x1 << (7 - posMod))));
}
// Increment position cursors
tempPos++;
if (posMod == 7)
{
posMod = 0;
posCount++;
}
else
{
posMod++;
}
if (readPosMod == 7)
{
readPosMod = 0;
readPosCount++;
}
else
{
readPosMod++;
}
}
this.Position = tempPos;
}
}