2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / corlib / Mono.Security.Cryptography / RSAManaged.cs
index 624b673f27f1b2f5309b35d0ad38347c37676be3..29ee75a21ced7c9ee8d28fe8ce1b0a1fd1271057 100644 (file)
@@ -226,7 +226,7 @@ namespace Mono.Security.Cryptography {
 
                        // 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);
+                       byte[] result = GetPaddedValue (output, (KeySize >> 3));
                        // zeroize values
                        input.Clear (); 
                        output.Clear ();
@@ -245,17 +245,19 @@ namespace Mono.Security.Cryptography {
                        BigInteger output = input.ModPow (e, n);
                        // 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);
+                       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 ();
@@ -277,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;
@@ -290,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);
@@ -313,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) 
@@ -462,10 +499,9 @@ namespace Mono.Security.Cryptography {
                        get { return (!keypairGenerated || isCRTpossible); }
                }
 
-               private byte[] GetPaddedValue (BigInteger value)
+               private byte[] GetPaddedValue (BigInteger value, int length)
                {
                        byte[] result = value.GetBytes ();
-                       int length = (KeySize >> 3);
                        if (result.Length >= length)
                                return result;