To prove that some data has come from someone we trust, it can be signed. Actually, you don't sign the data itself; instead, you sign a hash of the data. We will use the RSA algorithm combined with the SHA256 algorithm.
In the Ch11_CryptographyLib
class library project, add the following code to the Protector
class:
public static string PublicKey; public static string ToXmlString( this RSA rsa, bool includePrivateParameters) { var p = rsa.ExportParameters(includePrivateParameters); XElement xml; if (includePrivateParameters) { xml = new XElement("RSAKeyValue" , new XElement("Modulus", Convert.ToBase64String(p.Modulus)) , new XElement("Exponent", Convert.ToBase64String(p.Exponent)) , new XElement("P", Convert.ToBase64String(p.P)) , new XElement("Q", Convert.ToBase64String(p.Q)) , new XElement("DP", Convert.ToBase64String(p.DP)) , new XElement("DQ", Convert.ToBase64String(p.DQ)) , new XElement("InverseQ", Convert.ToBase64String(p.InverseQ)) ); } else { xml = new XElement("RSAKeyValue" , new XElement("Modulus", Convert.ToBase64String(p.Modulus)) , new XElement("Exponent", Convert.ToBase64String(p.Exponent)) ); } return xml?.ToString(); } public static void FromXmlString( this RSA rsa, string parametersAsXml) { var xml = XDocument.Parse(parametersAsXml); var root = xml.Element("RSAKeyValue"); var p = new RSAParameters { Modulus = Convert.FromBase64String( root.Element("Modulus").Value), Exponent = Convert.FromBase64String( root.Element("Exponent").Value) }; if(root.Element("P") != null) { p.P = Convert.FromBase64String(root.Element("P").Value); p.Q = Convert.FromBase64String(root.Element("Q").Value); p.DP = Convert.FromBase64String(root.Element("DP").Value); p.DQ = Convert.FromBase64String(root.Element("DQ").Value); p.InverseQ = Convert.FromBase64String( root.Element("InverseQ").Value); } rsa.ImportParameters(p); } public static string GenerateSignature(string data) { byte[] dataBytes = Encoding.Unicode.GetBytes(data); var sha = SHA256.Create(); var hashedData = sha.ComputeHash(dataBytes); var rsa = RSA.Create(); PublicKey = rsa.ToXmlString(false); // exclude private key return Convert.ToBase64String(rsa.SignHash(hashedData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } public static bool ValidateSignature( string data, string signature) { byte[] dataBytes = Encoding.Unicode.GetBytes(data); var sha = SHA256.Create(); var hashedData = sha.ComputeHash(dataBytes); byte[] signatureBytes = Convert.FromBase64String(signature); var rsa = RSA.Create(); rsa.FromXmlString(PublicKey); return rsa.VerifyHash(hashedData, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); }
Note the following:
ToXmlString
and FromXmlString
. These serialize and deserialize the RSAParameters
structure that contains the public and private keys. .NET Core's implementation of RSA does not include them.false
when we call the ToXmlString
method.SHA256
.Add a new console application project named Ch11_SigningApp
. Add a reference to the Ch11_CryptographyLib
assembly, and then import the following namespaces:
using static System.Console; using Packt.CS7;
In the Main
method, add the following code:
Write("Enter some text to sign: "); string data = ReadLine(); var signature = Protector.GenerateSignature(data); WriteLine($"Signature: {signature}"); WriteLine("Public key used to check signature:"); WriteLine(Protector.PublicKey); if (Protector.ValidateSignature(data, signature)) { WriteLine("Correct! Signature is valid."); } else { WriteLine("Invalid signature."); } // create a fake signature by replacing the // first character with an X var fakeSignature = signature.Replace(signature[0], 'X'); if (Protector.ValidateSignature(data, fakeSignature)) { WriteLine("Correct! Signature is valid."); } else { WriteLine($"Invalid signature: {fakeSignature}"); }
Run the console application and enter some text:
Enter some text to sign: The cat sat on the mat. Signature: LSmfgRuRRvYzM1/jg7U7jkKINCU4KKGpFUCvCB87hmWpa3gDVLjLj0Wift+CktZuPSkc/ gAnIzC1bQCOyELsrNWzATnPDFa/B0Gpy0vAJ8VJ9FPs1vFy353mMnGcnQU8fOummKgEv4 r1JpsnkJQ41MGUMNCH9YVodO6Bn6o81g0= Public key used to check signature: <RSAKeyValue><Modulus>qPnY4UHIqJMuUJ0CQ4F0Xy/fxaugNFFe/QNikGsufdKrwa1 t+CcQqCmWso4zUDW3NTFCWFGilisJ4SqTBgYee/VT9UGuFng68TrZXNiNJO8dP8OZHNBi rWkhtsNQx9A6rq9bZ/9dsjY1hYsWpGKCw4WhxsHjmGuevQew8C+I2z0=</Modulus><Ex ponent>AQAB</Exponent></RSAKeyValue> Correct! Signature is valid. Invalid signature: X1uDRfCDXvOyhMtqXlxqzSljhADD/81E0UonuVs9VfZ7ceuyFWh4O7rwkdc1+l25DzGf6 4swtbXZsukpSupFqvkAOIJ6XqMlD92vlG1nquereiWkshYnxxVts30QJIFKKyOTBTfN/V OljlZVMxT/RA6pggPtESlv+urDJT4z/PEtR5jdx+CTZHQc9WiceFbpuybyf/vEdddtF0T 7g8NeLKEPbT6b7CHGDM1HKbRqnSecv456QNfHNmEXxRk9MpI0DgQLnXpOhHcVwEFc6+dY 6kdNnWd6NIOY3qX6FT782t0lQ2swcWxF9fUcvWVSeC84EgVK447X9Xewkrf6CF7jxg==
18.218.224.226