2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / corlib / Mono.Security.Cryptography / RSAManaged.cs
index bc6b9fb045f4e5822028c41ff06b7b5647ebbe5d..29ee75a21ced7c9ee8d28fe8ce1b0a1fd1271057 100644 (file)
@@ -7,7 +7,7 @@
 //
 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
 // Portions (C) 2003 Ben Maurer
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004,2006 Novell, Inc (http://www.novell.com)
 //
 // Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
 // See bouncycastle.txt for license.
@@ -161,10 +161,10 @@ namespace Mono.Security.Cryptography {
                        get { return "RSA-PKCS1-KeyEx"; }
                }
 
-               // note: this property will exist in RSACryptoServiceProvider in
-               // version 2.0 of the framework
+               // note: when (if) we generate a keypair then it will have both
+               // the public and private keys
                public bool PublicOnly {
-                       get { return ((d == null) || (n == null)); }
+                       get { return (keypairGenerated && ((d == null) || (n == null))); }
                }
 
                public override string SignatureAlgorithm {
@@ -210,9 +210,11 @@ namespace Mono.Security.Cryptography {
                                        // m = m2 + q * h;
                                        output = m2 + q * h;
                                }
-                       } else {
+                       } else if (!PublicOnly) {
                                // m = c^d mod n
                                output = input.ModPow (d, n);
+                       } else {
+                               throw new CryptographicException (Locale.GetText ("Missing private key to decrypt value."));
                        }
 
                        if (keyBlinding) {
@@ -222,7 +224,9 @@ namespace Mono.Security.Cryptography {
                                r.Clear ();
                        }
 
-                       byte[] result = output.GetBytes ();
+                       // it's sometimes possible for the results to be a byte short
+                       // and this can break some software (see #79502) so we 0x00 pad the result
+                       byte[] result = GetPaddedValue (output, (KeySize >> 3));
                        // zeroize values
                        input.Clear (); 
                        output.Clear ();
@@ -239,17 +243,21 @@ namespace Mono.Security.Cryptography {
 
                        BigInteger input = new BigInteger (rgb);
                        BigInteger output = input.ModPow (e, n);
-                       byte[] result = output.GetBytes ();
+                       // it's sometimes possible for the results to be a byte short
+                       // and this can break some software (see #79502) so we 0x00 pad the result
+                       byte[] result = GetPaddedValue (output, (KeySize >> 3));
                        // zeroize value
                        input.Clear (); 
                        output.Clear ();
                        return result;
                }
 
+
+
                public override RSAParameters ExportParameters (bool includePrivateParameters) 
                {
                        if (m_disposed)
-                               throw new ObjectDisposedException ("");
+                               throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
 
                        if (!keypairGenerated)
                                GenerateKeyPair ();
@@ -271,11 +279,12 @@ namespace Mono.Security.Cryptography {
                                // but CRT parameters are optionals
                                if ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null)) {
                                        // and we include them only if we have them all
-                                       param.P = p.GetBytes ();
-                                       param.Q = q.GetBytes ();
-                                       param.DP = dp.GetBytes ();
-                                       param.DQ = dq.GetBytes ();
-                                       param.InverseQ = qInv.GetBytes ();
+                                       int length = (KeySize >> 4);
+                                       param.P = GetPaddedValue (p, length);
+                                       param.Q = GetPaddedValue (q, length);
+                                       param.DP = GetPaddedValue (dp, length);
+                                       param.DQ = GetPaddedValue (dq, length);
+                                       param.InverseQ = GetPaddedValue (qInv, length);
                                }
                        }
                        return param;
@@ -284,13 +293,13 @@ namespace Mono.Security.Cryptography {
                public override void ImportParameters (RSAParameters parameters) 
                {
                        if (m_disposed)
-                               throw new ObjectDisposedException ("");
+                               throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
 
                        // if missing "mandatory" parameters
                        if (parameters.Exponent == null) 
-                               throw new CryptographicException ("Missing Exponent");
+                               throw new CryptographicException (Locale.GetText ("Missing Exponent"));
                        if (parameters.Modulus == null)
-                               throw new CryptographicException ("Missing Modulus");
+                               throw new CryptographicException (Locale.GetText ("Missing Modulus"));
        
                        e = new BigInteger (parameters.Exponent);
                        n = new BigInteger (parameters.Modulus);
@@ -307,10 +316,44 @@ namespace Mono.Security.Cryptography {
                                p = new BigInteger (parameters.P);
                        if (parameters.Q != null)
                                q = new BigInteger (parameters.Q);
-                       
+
                        // we now have a keypair
                        keypairGenerated = true;
-                       isCRTpossible = ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null));
+                       bool privateKey = ((p != null) && (q != null) && (dp != null));
+                       isCRTpossible = (privateKey && (dq != null) && (qInv != null));
+
+                       // check if the public/private keys match
+                       // the way the check is made allows a bad D to work if CRT is available (like MS does, see unit tests)
+                       if (!privateKey)
+                               return;
+
+                       // always check n == p * q
+                       bool ok = (n == (p * q));
+                       if (ok) {
+                               // we now know that p and q are correct, so (p - 1), (q - 1) and phi will be ok too
+                               BigInteger pSub1 = (p - 1);
+                               BigInteger qSub1 = (q - 1);
+                               BigInteger phi = pSub1 * qSub1;
+                               // e is fairly static but anyway we can ensure it makes sense by recomputing d
+                               BigInteger dcheck = e.ModInverse (phi);
+
+                               // now if our new d(check) is different than the d we're provided then we cannot
+                               // be sure if 'd' or 'e' is invalid... (note that, from experience, 'd' is more 
+                               // likely to be invalid since it's twice as large as DP (or DQ) and sits at the
+                               // end of the structure (e.g. truncation).
+                               ok = (d == dcheck);
+
+                               // ... unless we have the pre-computed CRT parameters
+                               if (!ok && isCRTpossible) {
+                                       // we can override the previous decision since Mono always prefer, for 
+                                       // performance reasons, using the CRT algorithm
+                                       ok = (dp == (dcheck % pSub1)) && (dq == (dcheck % qSub1)) && 
+                                               (qInv == q.ModInverse (p));
+                               }
+                       }
+
+                       if (!ok)
+                               throw new CryptographicException (Locale.GetText ("Private/public key mismatch"));
                }
 
                protected override void Dispose (bool disposing) 
@@ -455,5 +498,19 @@ namespace Mono.Security.Cryptography {
                        // possible (in case the key was imported)
                        get { return (!keypairGenerated || isCRTpossible); }
                }
+
+               private byte[] GetPaddedValue (BigInteger value, int length)
+               {
+                       byte[] result = value.GetBytes ();
+                       if (result.Length >= length)
+                               return result;
+
+                       // left-pad 0x00 value on the result (same integer, correct length)
+                       byte[] padded = new byte[length];
+                       Buffer.BlockCopy (result, 0, padded, (length - result.Length), result.Length);
+                       // temporary result may contain decrypted (plaintext) data, clear it
+                       Array.Clear (result, 0, result.Length);
+                       return padded;
+               }
        }
 }