#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.IO; using System.Xml; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace CSharpTest.Net.Crypto { /// /// Provides a wrapper around encrypting with public keys from Certificats or CSP /// public class RSAPublicKey : AsymmetricKey { private readonly RSACryptoServiceProvider _rsaKey; /// Creates the key from the information provided public static RSAPublicKey FromParameters(RSAParameters parameters) { if (parameters.D != null) return new RSAPrivateKey(parameters); return new RSAPublicKey(parameters); } /// Creates the key from the information provided public static RSAPublicKey FromBytes(byte[] bytes) { RSACryptoServiceProvider key = new RSACryptoServiceProvider(); key.ImportCspBlob(bytes); if (key.PublicOnly) return new RSAPublicKey(key); return new RSAPrivateKey(key); } /// Creates the key from the information provided public static RSAPublicKey FromStore(string name) { CspParameters p = new CspParameters(); p.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey; p.KeyContainerName = name; return FromStore(p); } /// Creates the key from the information provided public static RSAPublicKey FromStore(CspParameters parameters) { RSACryptoServiceProvider key = new RSACryptoServiceProvider(parameters); if (key.PublicOnly) return new RSAPublicKey(key); return new RSAPrivateKey(key); } /// Creates the key from the information provided public static RSAPublicKey FromXml(string xml) { return RSAPublicKey.FromXml(new XmlTextReader(new StringReader(xml))); } /// Create RSAPublicKey with the provided key public RSAPublicKey(X509Certificate2 certificate) : this(Check.IsAssignable(certificate.PublicKey.Key)) { } /// Create RSAPublicKey with the provided key public RSAPublicKey(RSAParameters keyInfo) { Check.NotNull(keyInfo); Check.NotNull(keyInfo.Modulus); Check.NotNull(keyInfo.Exponent); _rsaKey = new RSACryptoServiceProvider(); _rsaKey.ImportParameters(keyInfo); } /// Create RSAPublicKey with the provided key public RSAPublicKey(RSACryptoServiceProvider keyInfo) { _rsaKey = keyInfo; } /// Clears the key protected override void Dispose(bool disposing) { _rsaKey.Clear(); base.Dispose(disposing); } /// Returns the key to use for encryption/decryption protected RSACryptoServiceProvider RSAKey { get { return Assert(_rsaKey); } } /// /// For this type of padding, block size is (key byte length - 11) /// see http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.encrypt.aspx /// protected override int BlockSize { get { return (_rsaKey.KeySize / 8) - 11; } } /// Output size protected override int TransformSize { get { return (_rsaKey.KeySize / 8); } } /// Encrypts the given bytes protected override byte[] EncryptBlock(byte[] blob) { return RSAKey.Encrypt(blob, false); } /// Decrypts the given bytes protected override byte[] DecryptBlock(byte[] blob) { Check.Assert(Assert(IsPrivateKey)); return RSAKey.Decrypt(blob, false); } /// Returns True if this object is also an RSAPrivateKey public bool IsPrivateKey { get { return this is RSAPrivateKey; } } /// Returns the public/private key information public RSAParameters ExportParameters() { return RSAKey.ExportParameters(IsPrivateKey); } /// Creates the key from the information provided public static RSAPublicKey FromXml(XmlReader xrdr) { RSAParameters param = new RSAParameters(); while (xrdr.Read()) { if (xrdr.NodeType == XmlNodeType.Element) { if (xrdr.LocalName == "Modulus") param.Modulus = Convert.FromBase64String(xrdr.ReadElementString()); if (xrdr.LocalName == "Exponent") param.Exponent = Convert.FromBase64String(xrdr.ReadElementString()); } } Check.Assert(param.Modulus != null && param.Exponent != null); return FromParameters(param); } /// Returns the key information public virtual void ToXml(XmlWriter xml) { RSAParameters param = ExportParameters(); xml.WriteStartElement("RSAKeyValue"); xml.WriteElementString("Modulus", Convert.ToBase64String(param.Modulus)); xml.WriteElementString("Exponent", Convert.ToBase64String(param.Exponent)); xml.WriteEndElement(); } /// Returns the key information public String ToXml() { using (StringWriter sw = new StringWriter()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.ConformanceLevel = ConformanceLevel.Document; settings.Encoding = System.Text.Encoding.ASCII; settings.CloseOutput = false; settings.Indent = true; settings.OmitXmlDeclaration = true; using (XmlWriter xwtr = XmlWriter.Create(sw, settings)) ToXml(xwtr); return sw.ToString(); } } /// Returns a CspBlob standard binary key definition public byte[] ToArray() { return RSAKey.ExportCspBlob(IsPrivateKey); } /// /// Writes a copy of this key into the local Csp store for the current user /// public void WriteToStore(string name) { WriteToStore(name, CspProviderFlags.NoPrompt); } /// /// Writes a copy of this key into the local Csp store with the given options /// public void WriteToStore(string name, CspProviderFlags flags) { CspParameters cp = new CspParameters(); cp.KeyContainerName = name; cp.Flags = flags; RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cp); csp.ImportCspBlob(RSAKey.ExportCspBlob(IsPrivateKey)); csp.PersistKeyInCsp = true; csp.Clear(); } /// /// Removes the key from the Csp store if it was fetch with RSAPublicKey.FromStore(...) /// public bool DeleteFromStore() { bool removing = RSAKey.PersistKeyInCsp; RSAKey.PersistKeyInCsp = false; return removing; } /// /// Signs the provided Hash code with the private key and returns the hash signature /// public bool VerifyHash(byte[] signature, Hash hashBytes) { return RSAKey.VerifyHash(hashBytes.ToArray(), hashBytes.AlgorithmOID, signature); } } }