File: system\security\cryptography\rsa.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 
 
//
//  RSA.cs
//
 
namespace System.Security.Cryptography {
    using System.IO;
    using System.Text;
    using System.Runtime.Serialization;
    using System.Security.Util;
    using System.Globalization;
    using System.Diagnostics.Contracts;
 
    // We allow only the public components of an RSAParameters object, the Modulus and Exponent
    // to be serializable.
    [Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
    public struct RSAParameters {
        public byte[]      Exponent;
        public byte[]      Modulus;
#if SILVERLIGHT
        public byte[] P;
        public byte[] Q;
        public byte[] DP;
        public byte[] DQ;
        public byte[] InverseQ;
        public byte[] D;
#else // SILVERLIGHT
        [NonSerialized] public byte[]      P;
        [NonSerialized] public byte[]      Q;
        [NonSerialized] public byte[]      DP;
        [NonSerialized] public byte[]      DQ;
        [NonSerialized] public byte[]      InverseQ;
        [NonSerialized] public byte[]      D;
#endif // SILVERLIGHT
    }
 
[System.Runtime.InteropServices.ComVisible(true)]
    public abstract class RSA : AsymmetricAlgorithm
    {
        protected RSA() { }
        //
        // public methods
        //
 
        new static public RSA Create() {
            return Create("System.Security.Cryptography.RSA");
        }
 
        new static public RSA Create(String algName) {
            return (RSA) CryptoConfig.CreateFromName(algName);
        }
 
        static public RSA Create(int keySizeInBits) {
            RSA rsa = (RSA)CryptoConfig.CreateFromName("RSAPSS");
            rsa.KeySize = keySizeInBits;
 
            if (rsa.KeySize != keySizeInBits) {
                throw new CryptographicException();
            }
 
            return rsa;
        }
 
        static public RSA Create(RSAParameters parameters) {
            RSA rsa = (RSA)CryptoConfig.CreateFromName("RSAPSS");
            rsa.ImportParameters(parameters);
            return rsa;
        }
 
        //
        // New RSA encrypt/decrypt/sign/verify RSA abstractions in .NET 4.6+ and .NET Core
        //
        // Methods that throw DerivedClassMustOverride are effectively abstract but we
        // cannot mark them as such as it would be a breaking change. We'll make them
        // abstract in .NET Core.
 
        public virtual byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
            throw DerivedClassMustOverride();
        }
 
        public virtual byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
            throw DerivedClassMustOverride();
        }
 
        public virtual byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            throw DerivedClassMustOverride();
        }
 
        public virtual bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            throw DerivedClassMustOverride();
        }
 
        protected virtual byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
            throw DerivedClassMustOverride();
        }
 
        protected virtual byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
            throw DerivedClassMustOverride();
        }
 
        public byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            if (data == null) {
                throw new ArgumentNullException("data");
            }
            return SignData(data, 0, data.Length, hashAlgorithm, padding);
        }
 
        public virtual byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            if (data == null) {
                throw new ArgumentNullException("data");
            }
            if (offset < 0 || offset > data.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (count < 0 || count > data.Length - offset) {
                throw new ArgumentOutOfRangeException("count");
            }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
                throw HashAlgorithmNameNullOrEmpty();
            }
            if (padding == null) {
                throw new ArgumentNullException("padding");
            }
 
            byte[] hash = HashData(data, offset, count, hashAlgorithm);
            return SignHash(hash, hashAlgorithm, padding);
        }
 
        public virtual byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            if (data == null) {
                throw new ArgumentNullException("data");
            }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
                throw HashAlgorithmNameNullOrEmpty();
            }
            if (padding == null) {
                throw new ArgumentNullException("padding");
            }
    
            byte[] hash = HashData(data, hashAlgorithm);
            return SignHash(hash, hashAlgorithm, padding);
        }
 
        public bool VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            if (data == null) {
                throw new ArgumentNullException("data");
            }
            return VerifyData(data, 0, data.Length, signature, hashAlgorithm, padding);
        }
 
        public virtual bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            if (data == null) {
                throw new ArgumentNullException("data");
            }
            if (offset < 0 || offset > data.Length) {
                throw new ArgumentOutOfRangeException("offset");
            }
            if (count < 0 || count > data.Length - offset) {
                throw new ArgumentOutOfRangeException("count"); 
            }
            if (signature == null) {
                throw new ArgumentNullException("signature");
            }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
                throw HashAlgorithmNameNullOrEmpty();
            }
            if (padding == null) {
                throw new ArgumentNullException("padding");
            }
 
            byte[] hash = HashData(data, offset, count, hashAlgorithm);
            return VerifyHash(hash, signature, hashAlgorithm, padding);
        }
 
        public bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
            if (data == null) {
                throw new ArgumentNullException("data");
            }
            if (signature == null) {
                throw new ArgumentNullException("signature");
            }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
                throw HashAlgorithmNameNullOrEmpty();
            }
            if (padding == null) {
                throw new ArgumentNullException("padding");
            }
 
            byte[] hash = HashData(data, hashAlgorithm);
            return VerifyHash(hash, signature, hashAlgorithm, padding);
        }
 
        private static Exception DerivedClassMustOverride() {
            return new NotImplementedException(Environment.GetResourceString("NotSupported_SubclassOverride"));
        }
 
        internal static Exception HashAlgorithmNameNullOrEmpty() {
            return new ArgumentException(Environment.GetResourceString("Cryptography_HashAlgorithmNameNullOrEmpty"), "hashAlgorithm");
        }
 
        //
        // Legacy encrypt/decrypt RSA abstraction from .NET < 4.6
        //
        // These should be obsolete, but we can't mark them as such here due to rules around not introducing
        // source breaks to scenarios that compile against the GAC.
        //
        // They used to be abstract, but the only concrete implementation in RSACryptoServiceProvider threw
        // NotSupportedException! This has been moved up to the base so all subclasses can ignore them moving forward.
        // They will also be removed from .NET Core altogether.
        //
        // The original intent was for these to perform the RSA algorithm without padding/depadding. This can
        // be seen by how the RSAXxx(De)Formatter classes call them in the non-RSACryptoServiceProvider case -- 
        // they do the padding/depadding in managed code.
        //
        // Unfortunately, these formatter classes are still incompatible with RSACng or any derived class that does not
        // implement EncryptValue, DecryptValue as the formatters speculatively expected non-RSACryptoServiceProvider
        // to do. That needs to be fixed in a subsequent release. We can still do it as it would move an exception to a
        // correct result...
        //
 
        // [Obsolete]
        public virtual byte[] DecryptValue(byte[] rgb) {
           throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
        }
 
        // [Obsolete]
        public virtual byte[] EncryptValue(byte[] rgb) {
           throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
       }
 
        //
        // These should also be obsolete (on the base). They aren't well defined nor are they used
        // anywhere in the FX apart from checking that they're not null.
        //
        // For new derived RSA classes, we'll just return "RSA" which is analagous to what ECDsa
        // and ECDiffieHellman do.
        //
        // Note that for compat, RSACryptoServiceProvider still overrides and returns RSA-PKCS1-KEYEX 
        // and http://www.w3.org/2000/09/xmldsig#rsa-sha1
        //
 
        public override string KeyExchangeAlgorithm {
            get { return "RSA"; }
        }
 
        public override string SignatureAlgorithm {
            get { return "RSA"; }
        }
 
 
        // Import/export functions
 
        // We can provide a default implementation of FromXmlString because we require 
        // every RSA implementation to implement ImportParameters
        // All we have to do here is parse the XML.
 
        public override void FromXmlString(String xmlString) {
            if (xmlString == null) throw new ArgumentNullException("xmlString");
            Contract.EndContractBlock();
            RSAParameters rsaParams = new RSAParameters();
            Parser p = new Parser(xmlString);
            SecurityElement topElement = p.GetTopElement();
 
            // Modulus is always present
            String modulusString = topElement.SearchForTextOfLocalName("Modulus");
            if (modulusString == null) {
                throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","RSA","Modulus"));
            }
            rsaParams.Modulus = Convert.FromBase64String(Utils.DiscardWhiteSpaces(modulusString));
 
            // Exponent is always present
            String exponentString = topElement.SearchForTextOfLocalName("Exponent");
            if (exponentString == null) {
                throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","RSA","Exponent"));
            }
            rsaParams.Exponent = Convert.FromBase64String(Utils.DiscardWhiteSpaces(exponentString));
 
            // P is optional
            String pString = topElement.SearchForTextOfLocalName("P");
            if (pString != null) rsaParams.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(pString));
 
            // Q is optional
            String qString = topElement.SearchForTextOfLocalName("Q");
            if (qString != null) rsaParams.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(qString));
 
            // DP is optional
            String dpString = topElement.SearchForTextOfLocalName("DP");
            if (dpString != null) rsaParams.DP = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dpString));
 
            // DQ is optional
            String dqString = topElement.SearchForTextOfLocalName("DQ");
            if (dqString != null) rsaParams.DQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dqString));
 
            // InverseQ is optional
            String inverseQString = topElement.SearchForTextOfLocalName("InverseQ");
            if (inverseQString != null) rsaParams.InverseQ = Convert.FromBase64String(Utils.DiscardWhiteSpaces(inverseQString));
 
            // D is optional
            String dString = topElement.SearchForTextOfLocalName("D");
            if (dString != null) rsaParams.D = Convert.FromBase64String(Utils.DiscardWhiteSpaces(dString));
 
            ImportParameters(rsaParams);
        }
 
        // We can provide a default implementation of ToXmlString because we require 
        // every RSA implementation to implement ImportParameters
        // If includePrivateParameters is false, this is just an XMLDSIG RSAKeyValue
        // clause.  If includePrivateParameters is true, then we extend RSAKeyValue with 
        // the other (private) elements.
        public override String ToXmlString(bool includePrivateParameters) {
            // From the XMLDSIG spec, RFC 3075, Section 6.4.2, an RSAKeyValue looks like this:
            /* 
               <element name="RSAKeyValue"> 
                 <complexType> 
                   <sequence>
                     <element name="Modulus" type="ds:CryptoBinary"/> 
                     <element name="Exponent" type="ds:CryptoBinary"/>
                   </sequence> 
                 </complexType> 
               </element>
            */
            // we extend appropriately for private components
            RSAParameters rsaParams = this.ExportParameters(includePrivateParameters);
            StringBuilder sb = new StringBuilder();
            sb.Append("<RSAKeyValue>");
            // Add the modulus
            sb.Append("<Modulus>"+Convert.ToBase64String(rsaParams.Modulus)+"</Modulus>");
            // Add the exponent
            sb.Append("<Exponent>"+Convert.ToBase64String(rsaParams.Exponent)+"</Exponent>");
            if (includePrivateParameters) {
                // Add the private components
                sb.Append("<P>"+Convert.ToBase64String(rsaParams.P)+"</P>");
                sb.Append("<Q>"+Convert.ToBase64String(rsaParams.Q)+"</Q>");
                sb.Append("<DP>"+Convert.ToBase64String(rsaParams.DP)+"</DP>");
                sb.Append("<DQ>"+Convert.ToBase64String(rsaParams.DQ)+"</DQ>");
                sb.Append("<InverseQ>"+Convert.ToBase64String(rsaParams.InverseQ)+"</InverseQ>");
                sb.Append("<D>"+Convert.ToBase64String(rsaParams.D)+"</D>");
            } 
            sb.Append("</RSAKeyValue>");
            return(sb.ToString());
        }
 
        abstract public RSAParameters ExportParameters(bool includePrivateParameters);
 
        abstract public void ImportParameters(RSAParameters parameters);
    }
}