Merge pull request #5636 from BrzVlad/fix-xmm-scan
[mono.git] / mcs / class / corlib / System.Security.Cryptography / RSACryptoServiceProvider.cs
index c254c17e5ba7a2663be01d5bc1d940e550e6802b..16fdfa15e5eb7a9d3b191fa793c83a036e558b7b 100644 (file)
@@ -29,8 +29,6 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if !NET_2_1 || MONOTOUCH
-
 using System.IO;
 using System.Runtime.InteropServices;
 
@@ -38,9 +36,10 @@ using Mono.Security.Cryptography;
 
 namespace System.Security.Cryptography {
 
-       [ComVisible (true)]
-       public sealed class RSACryptoServiceProvider : RSA, ICspAsymmetricAlgorithm {
+       public partial class RSACryptoServiceProvider {
                private const int PROV_RSA_FULL = 1;    // from WinCrypt.h
+               private const int AT_KEYEXCHANGE = 1;
+               private const int AT_SIGNATURE = 2;
 
                private KeyPairPersistence store;
                private bool persistKey;
@@ -52,6 +51,7 @@ namespace System.Security.Cryptography {
                private RSAManaged rsa;
        
                public RSACryptoServiceProvider ()
+                       : this (1024)
                {
                        // Here it's not clear if we need to generate a keypair
                        // (note: MS implementation generates a keypair in this case).
@@ -61,29 +61,31 @@ namespace System.Security.Cryptography {
                        // So we'll generate the keypair only when (and if) it's being
                        // used (or exported). This should save us a lot of time (at 
                        // least in the unit tests).
-                       Common (1024, null);
                }
        
                public RSACryptoServiceProvider (CspParameters parameters) 
+                       : this (1024, parameters)
                {
-                       Common (1024, parameters);
                        // no keypair generation done at this stage
                }
        
                public RSACryptoServiceProvider (int dwKeySize) 
                {
                        // Here it's clear that we need to generate a new keypair
-                       Common (dwKeySize, null);
+                       Common (dwKeySize, false);
                        // no keypair generation done at this stage
                }
        
                public RSACryptoServiceProvider (int dwKeySize, CspParameters parameters) 
                {
-                       Common (dwKeySize, parameters);
+                       bool has_parameters = parameters != null;
+                       Common (dwKeySize, has_parameters);
+                       if (has_parameters)
+                               Common (parameters);
                        // no keypair generation done at this stage
                }
        
-               private void Common (int dwKeySize, CspParameters p
+               void Common (int dwKeySize, bool parameters
                {
                        // Microsoft RSA CSP can do between 384 and 16384 bits keypair
                        LegalKeySizesValue = new KeySizes [1];
@@ -93,34 +95,32 @@ namespace System.Security.Cryptography {
                        rsa = new RSAManaged (KeySize);
                        rsa.KeyGenerated += new RSAManaged.KeyGeneratedEventHandler (OnKeyGenerated);
 
-                       persistKey = (p != null);
-                       if (p == null) {
-                               p = new CspParameters (PROV_RSA_FULL);
-#if NET_1_1
-                               if (useMachineKeyStore)
-                                       p.Flags |= CspProviderFlags.UseMachineKeyStore;
-#endif
-                               store = new KeyPairPersistence (p);
-                               // no need to load - it cannot exists
-                       }
-                       else {
-                               store = new KeyPairPersistence (p);
-                               store.Load ();
-                               if (store.KeyValue != null) {
-                                       persisted = true;
-                                       this.FromXmlString (store.KeyValue);
-                               }
-                       }
+                       persistKey = parameters;
+                       if (parameters)
+                               return;
+
+                       // no need to load - it cannot exists
+                       var p = new CspParameters (PROV_RSA_FULL);
+                       if (UseMachineKeyStore)
+                               p.Flags |= CspProviderFlags.UseMachineKeyStore;
+                       store = new KeyPairPersistence (p);
                }
 
-#if NET_1_1
-               private static bool useMachineKeyStore = false;
+               void Common (CspParameters p)
+               {
+                       store = new KeyPairPersistence (p);
+                       bool exists = store.Load ();
+                       bool required = (p.Flags & CspProviderFlags.UseExistingKey) != 0;
+                       privateKeyExportable = (p.Flags & CspProviderFlags.UseNonExportableKey) == 0;
+
+                       if (required && !exists)
+                               throw new CryptographicException ("Keyset does not exist");
 
-               public static bool UseMachineKeyStore {
-                       get { return useMachineKeyStore; }
-                       set { useMachineKeyStore = value; }
+                       if (store.KeyValue != null) {
+                               persisted = true;
+                               FromXmlString (store.KeyValue);
+                       }
                }
-#endif
        
                ~RSACryptoServiceProvider () 
                {
@@ -154,17 +154,18 @@ namespace System.Security.Cryptography {
                public bool PublicOnly {
                        get { return rsa.PublicOnly; }
                }
-       
-               public override string SignatureAlgorithm {
-                       get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
-               }
-       
+
                public byte[] Decrypt (byte[] rgb, bool fOAEP) 
                {
-#if NET_1_1
+                       if (rgb == null)
+                               throw new ArgumentNullException("rgb");
+
+                       // size check -- must be at most the modulus size
+                       if (rgb.Length > (KeySize / 8))
+                               throw new CryptographicException(Environment.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize / 8));
+                       
                        if (m_disposed)
                                throw new ObjectDisposedException ("rsa");
-#endif
                        // choose between OAEP or PKCS#1 v.1.5 padding
                        AsymmetricKeyExchangeDeformatter def = null;
                        if (fOAEP)
@@ -215,7 +216,21 @@ namespace System.Security.Cryptography {
                        if ((includePrivateParameters) && (!privateKeyExportable))
                                throw new CryptographicException ("cannot export private key");
 
-                       return rsa.ExportParameters (includePrivateParameters);
+                       var rsaParams = rsa.ExportParameters (includePrivateParameters);
+                       if (includePrivateParameters) {
+                               // we want an ArgumentNullException is only the D is missing, but a
+                               // CryptographicException if other parameters (CRT) are missings
+                               if (rsaParams.D == null) {
+                                       throw new ArgumentNullException ("Missing D parameter for the private key.");
+                               } else if ((rsaParams.P == null) || (rsaParams.Q == null) || (rsaParams.DP == null) ||
+                                       (rsaParams.DQ == null) || (rsaParams.InverseQ == null)) {
+                                       // note: we can import a private key, using FromXmlString,
+                                       // without the CRT parameters but we export it using ToXmlString!
+                                       throw new CryptographicException ("Missing some CRT parameters for the private key.");
+                               }
+                       }
+
+                       return rsaParams;
                }
        
                public override void ImportParameters (RSAParameters parameters) 
@@ -230,7 +245,7 @@ namespace System.Security.Cryptography {
 
                        HashAlgorithm hash = null;
                        if (halg is String)
-                               hash = HashAlgorithm.Create ((String)halg);
+                               hash = GetHashFromString ((string) halg);
                        else if (halg is HashAlgorithm)
                                hash = (HashAlgorithm) halg;
                        else if (halg is Type)
@@ -238,17 +253,32 @@ namespace System.Security.Cryptography {
                        else
                                throw new ArgumentException ("halg");
 
+                       if (hash == null)
+                               throw new ArgumentException (
+                                               "Could not find provider for halg='" + halg + "'.",
+                                               "halg");
+
                        return hash;
                }
+
+               private HashAlgorithm GetHashFromString (string name)
+               {
+                       HashAlgorithm hash = HashAlgorithm.Create (name);
+                       if (hash != null)
+                               return hash;
+                       try {
+                               return HashAlgorithm.Create (GetHashNameFromOID (name));
+                       } catch (CryptographicException e) {
+                               throw new ArgumentException (e.Message, "halg", e);
+                       }
+               }
        
                // NOTE: this method can work with ANY configured (OID in machine.config) 
                // HashAlgorithm descendant
                public byte[] SignData (byte[] buffer, object halg) 
                {
-#if NET_1_1
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
-#endif
                        return SignData (buffer, 0, buffer.Length, halg);
                }
        
@@ -273,19 +303,21 @@ namespace System.Security.Cryptography {
                private string GetHashNameFromOID (string oid) 
                {
                        switch (oid) {
-                               case "1.3.14.3.2.26":
-                                       return "SHA1";
-                               case "1.2.840.113549.2.5":
-                                       return "MD5";
-                               default:
-                                       throw new NotSupportedException (oid + " is an unsupported hash algorithm for RSA signing");
+                       case "1.3.14.3.2.26":
+                               return "SHA1";
+                       case "1.2.840.113549.2.5":
+                               return "MD5";
+                       case "2.16.840.1.101.3.4.2.1":
+                               return "SHA256";
+                       case "2.16.840.1.101.3.4.2.2":
+                               return "SHA384";
+                       case "2.16.840.1.101.3.4.2.3":
+                               return "SHA512";
+                       default:
+                               throw new CryptographicException (oid + " is an unsupported hash algorithm for RSA signing");
                        }
                }
 
-               // LAMESPEC: str is not the hash name but an OID
-               // NOTE: this method is LIMITED to SHA1 and MD5 like the MS framework 1.0 
-               // and 1.1 because there's no method to get a hash algorithm from an OID. 
-               // However there's no such limit when using the [De]Formatter class.
                public byte[] SignHash (byte[] rgbHash, string str) 
                {
                        if (rgbHash == null)
@@ -296,6 +328,29 @@ namespace System.Security.Cryptography {
                        return PKCS1.Sign_v15 (this, hash, rgbHash);
                }
 
+               byte[] SignHash(byte[] rgbHash, int calgHash)
+               {
+                       return PKCS1.Sign_v15 (this, InternalHashToHashAlgorithm (calgHash), rgbHash);
+               }
+
+               static HashAlgorithm InternalHashToHashAlgorithm (int calgHash)
+               {
+                       switch (calgHash) {
+                       case Constants.CALG_MD5:
+                               return MD5.Create ();
+                       case Constants.CALG_SHA1:
+                               return SHA1.Create ();
+                       case Constants.CALG_SHA_256:
+                               return SHA256.Create ();
+                       case Constants.CALG_SHA_384:
+                               return SHA384.Create ();
+                       case Constants.CALG_SHA_512:
+                               return SHA512.Create ();
+                       }
+
+                       throw new NotImplementedException (calgHash.ToString ());
+               }
+
                // NOTE: this method can work with ANY configured (OID in machine.config) 
                // HashAlgorithm descendant
                public bool VerifyData (byte[] buffer, object halg, byte[] signature) 
@@ -310,10 +365,6 @@ namespace System.Security.Cryptography {
                        return PKCS1.Verify_v15 (this, hash, toBeVerified, signature);
                }
        
-               // LAMESPEC: str is not the hash name but an OID
-               // NOTE: this method is LIMITED to SHA1 and MD5 like the MS framework 1.0 
-               // and 1.1 because there's no method to get a hash algorithm from an OID. 
-               // However there's no such limit when using the [De]Formatter class.
                public bool VerifyHash (byte[] rgbHash, string str, byte[] rgbSignature) 
                {
                        if (rgbHash == null) 
@@ -325,6 +376,11 @@ namespace System.Security.Cryptography {
                        HashAlgorithm hash = HashAlgorithm.Create (hashName);
                        return PKCS1.Verify_v15 (this, hash, rgbHash, rgbSignature);
                }
+
+               bool VerifyHash(byte[] rgbHash, int calgHash, byte[] rgbSignature)
+               {
+                       return PKCS1.Verify_v15 (this, InternalHashToHashAlgorithm (calgHash), rgbHash, rgbSignature);
+               }
        
                protected override void Dispose (bool disposing) 
                {
@@ -355,11 +411,11 @@ namespace System.Security.Cryptography {
                }
                // ICspAsymmetricAlgorithm
 
-               [MonoTODO ("Always return null")]
-               // FIXME: call into KeyPairPersistence to get details
                [ComVisible (false)]
                public CspKeyContainerInfo CspKeyContainerInfo {
-                       get { return null; }
+                       get {
+                               return new CspKeyContainerInfo(store.Parameters);
+                       }
                }
 
                [ComVisible (false)]
@@ -374,7 +430,7 @@ namespace System.Security.Cryptography {
                        // ALGID (bytes 4-7) - default is KEYX
                        // 00 24 00 00 (for CALG_RSA_SIGN)
                        // 00 A4 00 00 (for CALG_RSA_KEYX)
-                       blob [5] = 0xA4;
+                       blob [5] = (byte) (((store != null) && (store.Parameters.KeyNumber == AT_SIGNATURE)) ? 0x24 : 0xA4);
                        return blob;
                }
 
@@ -402,9 +458,12 @@ namespace System.Security.Cryptography {
                                        ImportParameters (rsap);
                                }
                        }
+
+                       var p = new CspParameters (PROV_RSA_FULL);
+                       p.KeyNumber = keyBlob [5] == 0x24 ? AT_SIGNATURE : AT_KEYEXCHANGE;
+                       if (UseMachineKeyStore)
+                               p.Flags |= CspProviderFlags.UseMachineKeyStore;
+                       store = new KeyPairPersistence (p);
                }
        }
 }
-
-#endif
-