//
// (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.
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 {
// 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) {
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 ();
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 ();
// 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;
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);
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)
// 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;
+ }
}
}