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</PlainText> //string strTextToEncrypt = Encoding.UTF8.GetString(textToEncrypt); //string strPlainTextElement = "<PlainText>" + strTextToEncrypt + "</PlainText>"; string strIvFactor = "<IV>" + iv + "</IV>"; string strPlainTextElement = "<PlainText>" + Convert.ToBase64String(textToEncrypt) + "</PlainText>"; 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: // <SignedData>output of step 3</SignedData> // <Signature>output of step 5</Signature> string strSignedData = "<SignedData>" + strDataToSign + "</SignedData>"; string strSignatureBlock = "<Signature>" + Convert.ToBase64String(signature) + "</Signature>"; // string strSignatureBlock = "<Signature>" + strSignature + "</Signature>"; 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: // <SignedData>data</SignedData> // <Signature>signature</Signature> string SignedDataElementTagOpen = "<SignedData>"; string SignedDataElementTagClose = "</SignedData>"; string SignatureElementTagOpen = "<Signature>"; string SignatureElementTagClose = "</Signature>"; 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: // <IV>initialization vector</IV> // <PlainText>base64-encoded plaintext</PlainText> string IvElementTagOpen = "<IV>"; string IvElementTagClose = "</IV>"; string PlainTextElementTagOpen = "<PlainText>"; string PlainTextElementTagClose = "</PlainText>"; 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; } } }