New tests.
[mono.git] / mcs / class / corlib / System.Security.Cryptography / RSACryptoServiceProvider.cs
index a61d9d19f866bc77f5fc1784b5c811ae1f20f2fe..ef607bc5fd28b0274dfcca13685009223baf6f17 100644 (file)
@@ -2,28 +2,52 @@
 // RSACryptoServiceProvider.cs: Handles an RSA implementation.
 //
 // Authors:
-//     Sebastien Pouliot (spouliot@motus.com)
+//     Sebastien Pouliot <sebastien@ximian.com>
 //     Ben Maurer (bmaurer@users.sf.net)
 //
 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
 // Portions (C) 2003 Ben Maurer
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
 //
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if !MOONLIGHT
 
-using System;
 using System.IO;
+using System.Runtime.InteropServices;
 
-using Mono.Math;
 using Mono.Security.Cryptography;
 
 namespace System.Security.Cryptography {
 
-       public sealed class RSACryptoServiceProvider : RSA {
-       
-               private CspParameters cspParams;
+       [ComVisible (true)]
+       public sealed class RSACryptoServiceProvider : RSA, ICspAsymmetricAlgorithm {
+               private const int PROV_RSA_FULL = 1;    // from WinCrypt.h
+
+               private KeyPairPersistence store;
+               private bool persistKey;
+               private bool persisted;
        
                private bool privateKeyExportable = true; 
-               private bool persistKey = false;
-               private bool m_disposed = false;
+               private bool m_disposed;
 
                private RSAManaged rsa;
        
@@ -40,7 +64,6 @@ namespace System.Security.Cryptography {
                        Common (1024, null);
                }
        
-               [MonoTODO("Missing keypair persistance using CspParameters")]
                public RSACryptoServiceProvider (CspParameters parameters) 
                {
                        Common (1024, parameters);
@@ -54,43 +77,45 @@ namespace System.Security.Cryptography {
                        // no keypair generation done at this stage
                }
        
-               // FIXME: We currently dont link with MS CAPI. Anyway this makes
-               // only sense in Windows - what do we do elsewhere ?
-               [MonoTODO("Missing keypair persistance using CspParameters")]
                public RSACryptoServiceProvider (int dwKeySize, CspParameters parameters) 
                {
                        Common (dwKeySize, parameters);
                        // no keypair generation done at this stage
                }
        
-               // FIXME: We currently dont link with MS CAPI. Anyway this makes
-               // only sense in Windows - what do we do elsewhere ?
                private void Common (int dwKeySize, CspParameters p) 
                {
-                       if (p == null) {
-                               cspParams = new CspParameters ();
-#if ! NET_1_0
-                               if (useMachineKeyStore)
-                                       cspParams.Flags |= CspProviderFlags.UseMachineKeyStore;
-#endif
-                               // TODO: set default values (for keypair persistance)
-                       }
-                       else
-                               cspParams = p;
-                               // FIXME: We'll need this to support some kind of persistance
-
                        // Microsoft RSA CSP can do between 384 and 16384 bits keypair
                        LegalKeySizesValue = new KeySizes [1];
                        LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
                        base.KeySize = dwKeySize;
 
                        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);
+                               }
+                       }
                }
 
-#if ! NET_1_0
+#if NET_1_1
                private static bool useMachineKeyStore = false;
 
-               [MonoTODO("Related to keypair persistance")]
                public static bool UseMachineKeyStore {
                        get { return useMachineKeyStore; }
                        set { useMachineKeyStore = value; }
@@ -115,11 +140,19 @@ namespace System.Security.Cryptography {
                                      return rsa.KeySize;
                        }
                }
-       
-               [MonoTODO("Related to keypair persistance")]
+
                public bool PersistKeyInCsp {
-                       get { return persistKey;  }
-                       set { persistKey = value; }
+                       get { return persistKey; }
+                       set {
+                               persistKey = value;
+                               if (persistKey)
+                                       OnKeyGenerated (rsa, null);
+                       }
+               }
+
+               [ComVisible (false)]
+               public bool PublicOnly {
+                       get { return rsa.PublicOnly; }
                }
        
                public override string SignatureAlgorithm {
@@ -149,6 +182,9 @@ namespace System.Security.Cryptography {
                // Using this method to decrypt data IS dangerous (and very slow).
                public override byte[] DecryptValue (byte[] rgb) 
                {
+                       if (!rsa.IsCrtPossible)
+                               throw new CryptographicException ("Incomplete private key - missing CRT.");
+
                        return rsa.DecryptValue (rgb);
                }
        
@@ -248,14 +284,15 @@ namespace System.Security.Cryptography {
 
                // 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 mathod to get a hash algorithm from an OID. 
+               // 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)
                                throw new ArgumentNullException ("rgbHash");
-       
-                       HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
+                       // Fx 2.0 defaults to the SHA-1
+                       string hashName = (str == null) ? "SHA1" : GetHashNameFromOID (str);
+                       HashAlgorithm hash = HashAlgorithm.Create (hashName);
                        return PKCS1.Sign_v15 (this, hash, rgbHash);
                }
 
@@ -263,10 +300,8 @@ namespace System.Security.Cryptography {
                // HashAlgorithm descendant
                public bool VerifyData (byte[] buffer, object halg, byte[] signature) 
                {
-#if NET_1_1
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
-#endif
                        if (signature == null)
                                throw new ArgumentNullException ("signature");
 
@@ -277,7 +312,7 @@ namespace System.Security.Cryptography {
        
                // 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 mathod to get a hash algorithm from an OID. 
+               // 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) 
                {
@@ -285,18 +320,91 @@ namespace System.Security.Cryptography {
                                throw new ArgumentNullException ("rgbHash");
                        if (rgbSignature == null)
                                throw new ArgumentNullException ("rgbSignature");
-       
-                       HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
+                       // Fx 2.0 defaults to the SHA-1
+                       string hashName = (str == null) ? "SHA1" : GetHashNameFromOID (str);
+                       HashAlgorithm hash = HashAlgorithm.Create (hashName);
                        return PKCS1.Verify_v15 (this, hash, rgbHash, rgbSignature);
                }
        
                protected override void Dispose (bool disposing) 
                {
-                       if (rsa != null)
-                               rsa.Clear ();
-                       // call base class 
-                       // no need as they all are abstract before us
-                       m_disposed = true;
+                       if (!m_disposed) {
+                               // the key is persisted and we do not want it persisted
+                               if ((persisted) && (!persistKey)) {
+                                       store.Remove ();        // delete the container
+                               }
+                               if (rsa != null)
+                                       rsa.Clear ();
+                               // call base class 
+                               // no need as they all are abstract before us
+                               m_disposed = true;
+                       }
+               }
+
+               // private stuff
+
+               private void OnKeyGenerated (object sender, EventArgs e) 
+               {
+                       // the key isn't persisted and we want it persisted
+                       if ((persistKey) && (!persisted)) {
+                               // save the current keypair
+                               store.KeyValue = this.ToXmlString (!rsa.PublicOnly);
+                               store.Save ();
+                               persisted = true;
+                       }
+               }
+               // ICspAsymmetricAlgorithm
+
+               [MonoTODO ("Always return null")]
+               // FIXME: call into KeyPairPersistence to get details
+               [ComVisible (false)]
+               public CspKeyContainerInfo CspKeyContainerInfo {
+                       get { return null; }
+               }
+
+               [ComVisible (false)]
+               public byte[] ExportCspBlob (bool includePrivateParameters)
+               {
+                       byte[] blob = null;
+                       if (includePrivateParameters)
+                               blob = CryptoConvert.ToCapiPrivateKeyBlob (this);
+                       else
+                               blob = CryptoConvert.ToCapiPublicKeyBlob (this);
+
+                       // 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;
+                       return blob;
+               }
+
+               [ComVisible (false)]
+               public void ImportCspBlob (byte[] keyBlob)
+               {
+                       if (keyBlob == null)
+                               throw new ArgumentNullException ("keyBlob");
+
+                       RSA rsa = CryptoConvert.FromCapiKeyBlob (keyBlob);
+                       if (rsa is RSACryptoServiceProvider) {
+                               // default (if no change are present in machine.config)
+                               RSAParameters rsap = rsa.ExportParameters (!(rsa as RSACryptoServiceProvider).PublicOnly);
+                               ImportParameters (rsap);
+                       } else {
+                               // we can't know from RSA if the private key is available
+                               try {
+                                       // so we try it...
+                                       RSAParameters rsap = rsa.ExportParameters (true);
+                                       ImportParameters (rsap);
+                               }
+                               catch {
+                                       // and fall back
+                                       RSAParameters rsap = rsa.ExportParameters (false);
+                                       ImportParameters (rsap);
+                               }
+                       }
                }
        }
 }
+
+#endif
+