using System;
using System.Text;
using CryptoSysPKI;
/*
**************************** COPYRIGHT NOTICE****************************
* Copyright (C) 2002-5 DI Management Services Pty Limited.
* All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
* Last updated:
* $Date: 2005/08/24 15:53:00 $
* This code is provided as a suggested C# interface to the CryptoSys PKI.
* Use at your own risk. Make your own tests and check that this code
* does what you expect. Please report any bugs to <www.di-mgt.com.au>
* This copyright notice must always be left intact.
************************ END OF COPYRIGHT NOTICE*************************
*/
namespace RsaExample
{
/// <summary>
/// Example code to demonstrate RSA methods:
/// RsaEncryptBytes,
/// RsaDecryptBytes,
/// RsaSignText, and
/// RsaVerifySignedText.
/// </summary>
class ShowExample
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
ShowEncryptAndDecrypt();
ShowSignAndVerify();
}
static void ShowEncryptAndDecrypt()
{
string certFile;
string priKeyFile;
byte[] outputData;
byte[] inputData;
StringBuilder sbPassword;
// ENCRYPTION
// Required input:
// (a) // byte array of "plaintext"
inputData = System.Text.Encoding.Default.GetBytes("Hello world!");
// (b) // Certificate (which has recipient's public key)
certFile = "BobRSASignByCarl.cer";
// Output: byte array of "ciphertext"
outputData = RsaEncryptBytes(inputData, certFile);
// Display output in hex format
Console.WriteLine("ENC={0}", Cnv.ToHex(outputData));
// DECRYPTION
// Required input:
// (a) // byte array of "ciphertext"
inputData = outputData;
// (b) // file containing encrypted private key
priKeyFile = "BobPrivRSAEncrypt.epk";
// (c) password for encrypted private key
// NB we use a StringBuilder for security reasons
sbPassword = new StringBuilder("password");
// Output: byte array of "plaintext"
outputData = RsaDecryptBytes(inputData, priKeyFile, sbPassword.ToString());
// Display output in hex format
Console.WriteLine("MSG(hex) ={0}", Cnv.ToHex(outputData));
// Convert back to a string (assuming we are expecting a string)
Console.WriteLine("MSG(Ansi)={0}", System.Text.Encoding.Default.GetString(outputData));
// Finally, wipe the password string
Wipe.String(sbPassword);
}
static void ShowSignAndVerify()
{
string certFile;
string priKeyFile;
string messageText;
byte[] inputData;
byte[] outputData;
StringBuilder sbPassword;
// SIGNING
// Required input:
// (a) text to be signed
messageText = "Hello world!";
// (b) file containing encrypted private key
priKeyFile = "AlicePrivRSASign.epk";
// (c) password
// NB we use a StringBuilder for security reasons
sbPassword = new StringBuilder("password");
// Output: signature block byte array
outputData = RsaSignText(messageText, priKeyFile, sbPassword.ToString());
// Display output in hex format
Console.WriteLine("SIG={0}", Cnv.ToHex(outputData));
// VERIFICATION
// Required input:
// (a) the RSA signature block
inputData = outputData;
// (b) Certificate (which has signer's public key)
certFile = "AliceRSASignByCarl.cer";
// (c) Copy of text that has been signed
// Output: verified or not
bool isok = RsaVerifySignedText(inputData, certFile, messageText);
Console.WriteLine("Verification {0}", (isok ? "OK" : "FAILED!"));
// Finally, wipe the password string
Wipe.String(sbPassword);
}
/// <summary>
/// Encrypts a message using RSA key from recipient's X.509 certificate
/// </summary>
/// <param name="inputData">Message in byte array</param>
/// <param name="certFile">Name of X.509 certificate file</param>
/// <returns>Encrypted data as byte array; or empty array if error</returns>
/// <remarks>Message must be at least xx bytes shorter than modulus length</remarks>
public static byte[] RsaEncryptBytes(byte[] inputData, string certFile)
{
StringBuilder sbPublicKey;
byte[] block;
byte[] outputData;
int klen;
// 1. Read the public key from the recipient's X.509 certificate
sbPublicKey = Rsa.GetPublicKeyFromCert(certFile);
if (sbPublicKey.Length == 0)
{
Console.WriteLine("ERROR reading certificate");
return new byte[0];
}
// 2. Make an RSA data block of same length in bytes as key
// using EME-PKCS1-v1_5 encoding
klen = Rsa.KeyBytes(sbPublicKey.ToString());
if (klen <= 0)
{
Console.WriteLine("ERROR: invalid public key");
return new byte[0];
}
block = Rsa.EncodeMsg(klen, inputData, Rsa.EncodeFor.Encryption);
// 3. Encrypt with RSA public key
outputData = Rsa.RawPublic(block, sbPublicKey.ToString());
if (outputData.Length == 0)
{
Console.WriteLine("ERROR: failed to encrypt");
return new byte[0];
}
// 4. Clean up
Wipe.Data(block);
Wipe.String(sbPublicKey);
// 5. Output "ciphertext"
return outputData;
}
/// <summary>
/// Decrypt RSA-encrypted message using key from recipient's private key file
/// </summary>
/// <param name="inputData">Encrypted data block</param>
/// <param name="priKeyFile">Name of recipient's private key file</param>
/// <param name="password">Password for encrypted private key</param>
/// <returns>Decrypted message data; or an empty array if error</returns>
public static byte[] RsaDecryptBytes(byte[] inputData, string priKeyFile, string password)
{
StringBuilder sbPrivateKey;
byte[] block;
byte[] outputData;
// 1. Read in the private key from the encrypted key file
sbPrivateKey = Rsa.ReadEncPrivateKey(priKeyFile, password);
if (sbPrivateKey.ToString().Length == 0)
{
Console.WriteLine("ERROR reading private key file");
return new byte[0];
}
// 2. Decrypt with private key
block = Rsa.RawPrivate(inputData, sbPrivateKey.ToString());
if (block.Length == 0)
{
Console.WriteLine("Decryption error");
return new byte[0];
}
// 3. Extract the message from the encryption block
outputData = Rsa.DecodeMsg(block, Rsa.EncodeFor.Encryption);
if (outputData.Length == 0)
{
Console.WriteLine("Decryption error");
return new byte[0];
}
// 4. Clean up - NB should do this on error, too.
Wipe.Data(block);
Wipe.String(sbPrivateKey);
// 5. Output "plaintext"
return outputData;
}
/// <summary>
/// Signs a message using the sender's private key
/// </summary>
/// <param name="messageText">Text of message to be signed</param>
/// <param name="priKeyFile">Name of sender's private key file</param>
/// <param name="password">Password for encrypyed private key file</param>
/// <returns>Signature block; or an empty array if error</returns>
public static byte[] RsaSignText(string messageText, string priKeyFile, string password)
{
StringBuilder sbPrivateKey;
byte[] msg;
byte[] block;
byte[] outputData;
int klen;
// 1. Read in the private key from the encrypted key file
sbPrivateKey = Rsa.ReadEncPrivateKey(priKeyFile, password);
if (sbPrivateKey.Length == 0)
{
Console.WriteLine("ERROR reading private key file");
return new byte[0];
}
// 2. Convert message string to byte array
msg = System.Text.Encoding.Default.GetBytes(messageText);
// 3. Make an RSA data block of same length in bytes as key
// using EMSA-PKCS1-v1_5 encoding
klen = Rsa.KeyBytes(sbPrivateKey.ToString());
if (klen <= 0)
{
Console.WriteLine("ERROR reading private key file");
return new byte[0];
}
block = Rsa.EncodeMsg(klen, msg, Rsa.EncodeFor.Signature);
if (block.Length == 0)
{
Console.WriteLine("ERROR: could not encode data block");
return new byte[0];
}
// 4. Sign with RSA private key
outputData = Rsa.RawPrivate(block, sbPrivateKey.ToString());
// 5. Clear up
Wipe.Data(block);
Wipe.Data(msg);
Wipe.String(sbPrivateKey);
// 6. Output signature block
return outputData;
}
/// <summary>
/// Verifies whether or not an RSA signature is valid
/// </summary>
/// <param name="sigBlock">RSA signature block</param>
/// <param name="certFile">Name of sender's X.509 certificate file</param>
/// <param name="messageText">Text of message that has been signed</param>
/// <returns><b>true</b> if signature is valid; <b>false</b> otherwise</returns>
public static bool RsaVerifySignedText(byte[] sigBlock, string certFile, string messageText)
{
StringBuilder sbPublicKey;
byte[] msg;
byte[] block;
byte[] bcheck;
int klen;
bool isok;
// 1. Read the public key from the recipient's X.509 certificate
sbPublicKey = Rsa.GetPublicKeyFromCert(certFile);
if (sbPublicKey.Length == 0)
{
Console.WriteLine("ERROR reading certificate");
return false;
}
// 2. Check that signature block and key length are identical
klen = Rsa.KeyBytes(sbPublicKey.ToString());
if (klen != sigBlock.Length)
{
Console.WriteLine("ERROR: RSA signature and key are different lengths");
return false;
}
// 3. Decrypt signature block with RSA public key
block = Rsa.RawPublic(sigBlock, sbPublicKey.ToString());
// 4. Create an independent Encoded block from message
msg = System.Text.Encoding.Default.GetBytes(messageText);
bcheck = Rsa.EncodeMsg(klen, msg, Rsa.EncodeFor.Signature);
// And compare to see if they are the same
// using a home-grown byte comparison function
isok = ByteArraysAreEqual(bcheck, block);
// 5. Clean up
Wipe.Data(bcheck);
Wipe.Data(block);
Wipe.Data(msg);
Wipe.String(sbPublicKey);
// 6. Output whether verified or not
return isok;
}
private static bool ByteArraysAreEqual(byte[] data1, byte[] data2)
{ // Thanks to Jon Skeet http://www.pobox.com/~skeet
// If both are null, they're equal
if (data1==null && data2==null)
{
return true;
}
// If either but not both are null, they're not equal
if (data1==null || data2==null)
{
return false;
}
if (data1.Length != data2.Length)
{
return false;
}
for (int i=0; i < data1.Length; i++)
{
if (data1[i] != data2[i])
{
return false;
}
}
return true;
}
}
}