using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
namespace Brassring.Utilities
{
///
/// Summary description for RSA.
///
public class RSA
{
public RSA()
{
//
// TODO: Add constructor logic here
//
}
//*************************************************************
//************ ENCRYPTION / DECRYPTION ROUTINES ***************
//*************************************************************
//Encrypt a string and return an encrypted string
public string Encrypt(string Data, string PublicKey)
{
byte [] DataBytes = Encoding.UTF8.GetBytes(Data);
byte [] CryptBytes = Encrypt(ref DataBytes, PublicKey);
return Encoding.UTF8.GetString(CryptBytes);
}
//Encrypt a string and return an encrypted byte array
public byte [] EncryptToByte(string Data, string PublicKey)
{
byte [] DataBytes = Encoding.UTF8.GetBytes(Data);
byte [] CryptBytes = Encrypt(ref DataBytes, PublicKey);
return CryptBytes;
}
//Decrypt a string and return the decrypted string
public string Decrypt(string Data, string PrivateKey)
{
byte[] DataBytes = Encoding.UTF8.GetBytes(Data);
byte[] CleanBytes = Decrypt(ref DataBytes, PrivateKey);
return Encoding.UTF8.GetString(CleanBytes);
}
//Decrypt a byte array and return the decrypted string
public string Decrypt(byte[] Data, string PrivateKey)
{
byte[] CleanBytes = Decrypt(ref Data, PrivateKey);
return Encoding.UTF8.GetString(CleanBytes);
}
//Encrypt the contents of a file and put the encrypted results in another file
public void EncryptFile(string SourceFilePath, string DestFilePath, string publicKey)
{
FileStream SourceStream = new FileStream(SourceFilePath, FileMode.Open);
FileStream DestStream = new FileStream(DestFilePath, FileMode.Create);
try
{
Encrypt(SourceStream, DestStream, publicKey);
}
finally
{
SourceStream.Close();
DestStream.Close();
}
return;
}
//Decrypt the contents of a file and put the decrypted results in another file
public void DecryptFile(string SourceFilePath, string DestFilePath, string PrivateKey)
{
FileStream SourceStream = new FileStream(SourceFilePath, FileMode.Open);
FileStream DestStream = new FileStream(DestFilePath, FileMode.Create);
try
{
Decrypt(SourceStream, DestStream, PrivateKey);
}
finally
{
SourceStream.Close();
DestStream.Close();
}
}
//Encrypt a byte array an return the encrypted byte array
public byte [] Encrypt(ref byte [] Data, string publicKey)
{
RSACryptoServiceProvider RsaEnc = InstantiateRSAProvider(publicKey);
//NOTE: The maximum you can encrypt with RSA on a single call is is (keysize/8) - 11.
//If you need to encrypt more data than this, you//ll need to break the data up into
//chunks that are keysize/8 - 11 in size. Another problem with MS RSA provider is that
//when loading a key from XML, the KeySize attribute does not change in the Encrypter.
//This means that you have to get the RSAParameters for the public key and get the length
//of the modulus (this is keysize/8), then subtract 11 from that number to get the max amount
//to encrypt
RSAParameters rp = RsaEnc.ExportParameters(false);
int MaxLen = rp.Modulus.Length - 11;
int srcOff = 0;
int leftToEncrypt = Data.Length;
int encryptAmount = 0;
// cryptResult() Byte
System.IO.MemoryStream cryptResult = new System.IO.MemoryStream();
try
{
while (srcOff < Data.Length)
{
if (leftToEncrypt >= MaxLen)
{
encryptAmount = MaxLen;
}
else
{
encryptAmount = leftToEncrypt;
}
leftToEncrypt = leftToEncrypt - encryptAmount;
byte [] DataToCrypt = new byte[encryptAmount];
Array.Copy(Data, srcOff, DataToCrypt, 0, encryptAmount);
string tempBuf = DataToCrypt.ToString();
int tempBuffSize = DataToCrypt.Length;
byte [] cryptBytes = RsaEnc.Encrypt(DataToCrypt, false);
cryptResult.Write(cryptBytes, 0, cryptBytes.Length);
srcOff = srcOff + encryptAmount;
}
}
finally
{
cryptResult.Close();
}
// byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(DataToCrypt);
// return Convert.ToBase64String(cipherbytes);
return cryptResult.ToArray();
}
//Decrypt a byte array and return the decrypted byte array
public byte [] Decrypt(ref byte [] Data, string PrivateKey)
{
RSACryptoServiceProvider RsaEnc = InstantiateRSAProvider(PrivateKey);
//NOTE: The maximum you can decrypt with RSA on a single call is is (keysize/8).
//if you need to decrypt more data than this, you//ll need to break the data up into
//chunks that are keysize/8 in size. Another problem with MS RSA provider is that
//when loading a key from XML, the KeySize attribute does not change in the Encrypter.
//This means that you have to get the RSAParameters for the private key and get the length
//of the modulus (this is keysize/8) to get the max amount to decrypt.
RSAParameters rp = RsaEnc.ExportParameters(true);
int MaxLen = rp.Modulus.Length;
int srcOff = 0;
int leftToDecrypt = Data.Length;
int decryptAmount = 0;
System.IO.MemoryStream cleanResult = new System.IO.MemoryStream();
try
{
while (srcOff < Data.Length)
{
if (leftToDecrypt >= MaxLen)
{
decryptAmount = MaxLen;
}
else
{
decryptAmount = leftToDecrypt;
}
leftToDecrypt = leftToDecrypt - decryptAmount;
byte [] DataToDecrypt = new byte[decryptAmount];
Array.Copy(Data, srcOff, DataToDecrypt, 0, decryptAmount);
byte [] cleanBytes = RsaEnc.Decrypt(DataToDecrypt, false);
cleanResult.Write(cleanBytes, 0, cleanBytes.Length);
srcOff = srcOff + decryptAmount;
}
}
finally
{
cleanResult.Close();
}
return cleanResult.ToArray();
}
public void Encrypt(FileStream SourceStream, FileStream DestStream, string publicKey)
{
RSACryptoServiceProvider RsaEnc = InstantiateRSAProvider(publicKey);
RSAParameters rp = RsaEnc.ExportParameters(false);
int BufSize = (rp.Modulus.Length - 11) * 10;
byte [] Buffer = new byte[BufSize];
int AmountRead = SourceStream.Read(Buffer, 0, BufSize);
while (AmountRead > 0)
{
byte [] TempBuf = new byte [AmountRead - 1];
Array.Copy(Buffer, 0, TempBuf, 0, AmountRead);
byte [] Crypt = Encrypt(ref TempBuf, publicKey);
DestStream.Write(Crypt, 0, Crypt.Length);
AmountRead = SourceStream.Read(Buffer, 0, BufSize);
}
}
public void Decrypt(FileStream SourceStream, FileStream DestStream, string PrivateKey)
{
RSACryptoServiceProvider RsaEnc = InstantiateRSAProvider(PrivateKey);
RSAParameters rp = RsaEnc.ExportParameters(true);
int BufSize = rp.Modulus.Length * 10;
byte [] Buffer = new byte [BufSize];
int AmountRead = SourceStream.Read(Buffer, 0, BufSize);
while (AmountRead > 0)
{
byte [] TempBuf = new byte [AmountRead - 1];
Array.Copy(Buffer, 0, TempBuf, 0, AmountRead);
byte [] Clean = Decrypt(ref TempBuf, PrivateKey);
DestStream.Write(Clean, 0, Clean.Length);
AmountRead = SourceStream.Read(Buffer, 0, BufSize);
}
}
//*********************************************************
//*************** SIGN / VERIFY METHODS *******************
//*********************************************************
//Sign some data; we use the SHA-1. return the signature.
public byte [] SignData(ref byte[] Data, string PrivateKey)
{
RSACryptoServiceProvider Rsign = InstantiateRSAProvider(PrivateKey);
return Rsign.SignData(Data, "SHA1");
}
//Verify some signed data; we use SHA-1. return a boolean indicating if the data is verified.
public bool VerifyData(ref byte[] Data, ref byte [] Signature, string publicKey)
{
RSACryptoServiceProvider RsaVer = InstantiateRSAProvider(publicKey);
return RsaVer.VerifyData(Data, "SHA1", Signature);
}
//Sign some data; we use the SHA-1. return the signature a string.
//This method can be used by VBScript
public string SignStringData(string Data, string PrivateKey)
{
byte[] DataBytes = Encoding.UTF8.GetBytes(Data);
byte[] Sig = SignData(ref DataBytes, PrivateKey);
return Encoding.UTF8.GetString(Sig);
}
//Verify some signed data; we use SHA-1. return a boolean indicating if the data is verified.
//This method can be used by VBScript
public bool VerifyStringData(string Data, string Signature, string publicKey)
{
byte [] DataBytes = Encoding.UTF8.GetBytes(Data);
byte [] SigBytes = Encoding.UTF8.GetBytes(Signature);
return VerifyData(ref DataBytes, ref SigBytes, publicKey);
}
//Encrypt and sign some data; return a string token representing the encrypted data and signature
public string EncryptAndSign(ref byte [] textToEncrypt, string RecipientPublicKey, string SenderPrivateKey, string iv)
{
//1. Select an arbitrary string of XML-safe (no angle brackets, ampersands, or whitespace)
// ASCII characters as the initialization vector. For example, a random integer or a
// timestamp could be used. Base 64 encoded
//
//2. Base64-encode the plaintext being encrypted.
//
//3. Build this XML fragment (whitespace here and in the following steps is for
// readability and should not be included in the actual structure):
// put initialization vector here
// put base64-encoded plaintext here
//string strTextToEncrypt = Encoding.UTF8.GetString(textToEncrypt);
//string strPlainTextElement = "" + strTextToEncrypt + "";
string strIvFactor = "" + iv + "";
string strPlainTextElement = "" + Convert.ToBase64String(textToEncrypt) + "";
string strDataToSign = strIvFactor + strPlainTextElement;
//4. Generate PKCS1v1.5 signature of the output of step 3 using your private key.
byte [] dataToSign = Encoding.UTF8.GetBytes(strDataToSign);
byte [] signature = SignData(ref dataToSign, SenderPrivateKey);
//string strSignature = "singature place holder";
string strSignature = Encoding.UTF8.GetString(signature);
//5. Base64-encode the output of step 4.
//6. Construct this XML fragment:
// output of step 3
// output of step 5
string strSignedData = "" + strDataToSign + "";
string strSignatureBlock = "" + Convert.ToBase64String(signature) + "";
// string strSignatureBlock = "" + strSignature + "";
string strDataToEncrypt = strSignedData + strSignatureBlock;
//7. Encrypt the entire XML fragment using the recipient’s public key.
byte [] dataToEncrypt = Encoding.UTF8.GetBytes(strDataToEncrypt);
byte [] Crypt = Encrypt(ref dataToEncrypt, RecipientPublicKey);
//8. Base64-encode the output of step 7.
string encryptedResult = Convert.ToBase64String(Crypt);
return encryptedResult;
}
//Decrypt and verify some data; Parse the token containing encrypted data/sig. if the sig is verified,
//return the decrypted data.
public byte [] DecryptAndVerify(string Token, string SenderPublicKey, string RecipientPrivateKey, string vector)
{
//1. Base64-Decode the input.
// string strToken = Token;
byte[] dataToDecrypt = Convert.FromBase64String(Token);
//2. Decrypt the entire XML fragment using the BrassRing Private key.
byte [] decryptedData = Decrypt(ref dataToDecrypt, RecipientPrivateKey);
string strDecryptedData = Encoding.UTF8.GetString(decryptedData);
//3. Parse this XML fragment for:
// data
// signature
string SignedDataElementTagOpen = "";
string SignedDataElementTagClose = "";
string SignatureElementTagOpen = "";
string SignatureElementTagClose = "";
string SignatureDelimiter = SignedDataElementTagClose + SignatureElementTagOpen;
// strip off element tags to isolate signed data
int SignedDataElementTagOpenIndex = strDecryptedData.IndexOf(SignedDataElementTagOpen);
string strSignedData = strDecryptedData.Substring(0, strDecryptedData.IndexOf(SignedDataElementTagClose));
strSignedData = strSignedData.Replace(SignedDataElementTagOpen,"");
// strip off element tags to isolate signature
string strSignature = strDecryptedData.Substring(strDecryptedData.IndexOf(SignatureElementTagOpen));
strSignature = strSignature.Replace(SignatureElementTagOpen,"").Replace(SignatureElementTagClose,"");
//4. Base64 Decode the signature
//5. Authenticate PKCS1v1.5 signature with sender public key.
if (strSignature != "")
{
byte [] Signature = Convert.FromBase64String(strSignature);
byte [] SignedData = Encoding.UTF8.GetBytes(strSignedData);
if (VerifyData(ref SignedData, ref Signature, SenderPublicKey))
{
return ParseSignedDataBlock(strSignedData, vector);
}
else
{
throw new Exception("RSAEncryption.DecryptAndVerify: The signature could not be verified. Either the data and/or signature is corrupt or the wrong publicKey was used during the verify process");
}
}
else return Encoding.UTF8.GetBytes("");
}
//
// Parse Signed Data
//
private byte [] ParseSignedDataBlock(string strSignedData, string vector)
{
//6. Parse signed data XML fragment:
// initialization vector
// base64-encoded plaintext
string IvElementTagOpen = "";
string IvElementTagClose = "";
string PlainTextElementTagOpen = "";
string PlainTextElementTagClose = "";
string PlainTextDelimiter = IvElementTagClose + PlainTextElementTagOpen;
// strip off element tags to isolate IV Factor
int IvElementTagOpenIndex = strSignedData.IndexOf(IvElementTagOpen);
string strIv = strSignedData.Substring(0, strSignedData.IndexOf(IvElementTagClose));
strIv = strIv.Replace(IvElementTagOpen,"");
// strip off element tags to isolate base 64 encoded plain text
string strPlainText = strSignedData.Substring(strSignedData.IndexOf(PlainTextElementTagOpen));
strPlainText = strPlainText.Replace(PlainTextElementTagOpen,"").Replace(PlainTextElementTagClose,"");
//7. Base64 Decode plain text
byte [] plainText = Convert.FromBase64String(strPlainText);
//8. Compare IV vector
if ((strIv == vector) || (vector == "") || (vector == null))
{
return plainText;
}
else
{
throw new Exception("RSAEncryption.DecryptAndVerify: The IV factor does not match.");
}
}
//Encrypt and sign some data; return a string token representing the encrypted data and signature
//This method accepts the data a string so it can be used by VBScript
public string EncryptAndSignString(string strDataToEncrypt, string RecipientPublicKey, string SenderPrivateKey, string iv)
{
byte [] DataBytes = Encoding.UTF8.GetBytes(strDataToEncrypt);
return EncryptAndSign(ref DataBytes, RecipientPublicKey, SenderPrivateKey, iv);
}
//Decrypt and verify some data; Parse the token containing encrypted data/sig. if the sig is verified,
//return the decrypted data string. This method returns a string so it can be used by VBScript
public string DecryptAndVerifyString(string Token, string SenderPublicKey, string RecipientPrivateKey, string vector)
{
//Decrypt data
byte [] Clean = DecryptAndVerify(Token, SenderPublicKey, RecipientPrivateKey, vector);
return Encoding.UTF8.GetString(Clean);
}
//****************************************************
//****************** TOKEN METHODS *******************
//****************************************************
//Create a Token Form encrypted bytes HEX
public string CreateToken(byte [] EncryptedData)
{
string sb = "";;
int i = 0;
string StrHex = "";
while (i < EncryptedData.Length)
{
// string StrHex = Hex(EncryptedData[i]);
StrHex = EncryptedData[i].ToString("X");
if (StrHex.Length == 1)
{
StrHex = "0" + StrHex;
}
sb += StrHex;
i = i + 1;
}
return sb;
}
//Create a token from an encrypted string
public string CreateToken(string EncryptedString)
{
return CreateToken(Encoding.UTF8.GetBytes(EncryptedString));
}
//Parse the token into a byte array
public byte [] ParseTokenIntoBytes(string Token)
{
//build encrypted data byte array from token
int arraySize = 0;
int i = 0;
int pos = 0;
arraySize = (Token.Length / 2);
byte [] myData = new byte [arraySize];
while (i < myData.Length)
{
myData[i] = Convert.ToByte(Token.Substring(pos, 2), 16);
i = i + 1;
pos = pos + 2;
}
return myData;
}
//Parse the token into a string
public string ParseTokenIntoString(string Token)
{
return Encoding.UTF8.GetString(ParseTokenIntoBytes(Token));
}
// Convert a string to a byte array.
public static byte[] StrToByteArray(string str)
{
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
return encoding.GetBytes(str);
}
//*****************************************************************
//*************** PRIVATE HELPER FUNCTIONS ************************
//*****************************************************************
//Read an XMLKey file
private string ReadXMLKeyFile(string KeyFilePath)
{
System.IO.FileInfo xmlFile = new System.IO.FileInfo(KeyFilePath);
StreamReader reader = xmlFile.OpenText();
string Key = reader.ReadToEnd();
reader.Close();
return Key;
}
//Initialize the .NET RSACryptoServiceProvider
private RSACryptoServiceProvider InstantiateRSAProvider(string Key)
{
//-- Instantiate RSA crypto provider, and load private or public key
CspParameters cspParam = new CspParameters();
cspParam.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider RsaEnc = new RSACryptoServiceProvider(cspParam);
RsaEnc.FromXmlString(Key);
return RsaEnc;
}
}
}