//
// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
// Portions (C) 2003 Ben Maurer
-// (C) 2004 Novell (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.
//
-
-//
-// Copyright (C) 2004 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
using System;
using System.Security.Cryptography;
+using System.Text;
using Mono.Math;
private const int defaultKeySize = 1024;
private bool isCRTpossible = false;
+ private bool keyBlinding = true;
private bool keypairGenerated = false;
private bool m_disposed = false;
public RSAManaged (int keySize)
{
- KeySizeValue = keySize;
LegalKeySizesValue = new KeySizes [1];
LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
+ base.KeySize = keySize;
}
~RSAManaged ()
get { return "RSA-PKCS1-KeyEx"; }
}
- // note: this property will exist in RSACryptoServiceProvider in
- // version 1.2 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 {
GenerateKeyPair ();
BigInteger input = new BigInteger (rgb);
+ BigInteger r = null;
+
+ // we use key blinding (by default) against timing attacks
+ if (keyBlinding) {
+ // x = (r^e * g) mod n
+ // *new* random number (so it's timing is also random)
+ r = BigInteger.GenerateRandom (n.BitCount ());
+ input = r.ModPow (e, n) * input % n;
+ }
+
BigInteger output;
// decrypt (which uses the private key) can be
// optimized by using CRT (Chinese Remainder Theorem)
// thanks to benm!
h = p - ((m2 - m1) * qInv % p);
output = m2 + q * h;
- }
- else {
+ } else {
// h = (m1 - m2) * qInv mod p
h = (m1 - m2) * qInv % p;
// 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."));
}
- byte[] result = output.GetBytes ();
- // zeroize value
+
+ if (keyBlinding) {
+ // Complete blinding
+ // x^e / r mod n
+ output = output * r.ModInverse (n) % n;
+ r.Clear ();
+ }
+
+ // 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 ();
return result;
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 ();
param.Modulus = n.GetBytes ();
if (includePrivateParameters) {
// some parameters are required for exporting the private key
- if ((d == null) || (p == null) || (q == null))
+ if (d == null)
throw new CryptographicException ("Missing private key");
param.D = d.GetBytes ();
// hack for bugzilla #57941 where D wasn't provided
Buffer.BlockCopy (param.D, 0, normalizedD, (normalizedD.Length - param.D.Length), param.D.Length);
param.D = normalizedD;
}
- param.P = p.GetBytes ();
- param.Q = q.GetBytes ();
// but CRT parameters are optionals
- if ((dp != null) && (dq != null) && (qInv != null)) {
+ if ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null)) {
// and we include them only if we have them all
- 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)
public delegate void KeyGeneratedEventHandler (object sender, EventArgs e);
public event KeyGeneratedEventHandler KeyGenerated;
+
+ public override string ToXmlString (bool includePrivateParameters)
+ {
+ StringBuilder sb = new StringBuilder ();
+ RSAParameters rsaParams = ExportParameters (includePrivateParameters);
+ try {
+ sb.Append ("<RSAKeyValue>");
+
+ sb.Append ("<Modulus>");
+ sb.Append (Convert.ToBase64String (rsaParams.Modulus));
+ sb.Append ("</Modulus>");
+
+ sb.Append ("<Exponent>");
+ sb.Append (Convert.ToBase64String (rsaParams.Exponent));
+ sb.Append ("</Exponent>");
+
+ if (includePrivateParameters) {
+ if (rsaParams.P != null) {
+ sb.Append ("<P>");
+ sb.Append (Convert.ToBase64String (rsaParams.P));
+ sb.Append ("</P>");
+ }
+ if (rsaParams.Q != null) {
+ sb.Append ("<Q>");
+ sb.Append (Convert.ToBase64String (rsaParams.Q));
+ sb.Append ("</Q>");
+ }
+ if (rsaParams.DP != null) {
+ sb.Append ("<DP>");
+ sb.Append (Convert.ToBase64String (rsaParams.DP));
+ sb.Append ("</DP>");
+ }
+ if (rsaParams.DQ != null) {
+ sb.Append ("<DQ>");
+ sb.Append (Convert.ToBase64String (rsaParams.DQ));
+ sb.Append ("</DQ>");
+ }
+ if (rsaParams.InverseQ != null) {
+ sb.Append ("<InverseQ>");
+ sb.Append (Convert.ToBase64String (rsaParams.InverseQ));
+ sb.Append ("</InverseQ>");
+ }
+ sb.Append ("<D>");
+ sb.Append (Convert.ToBase64String (rsaParams.D));
+ sb.Append ("</D>");
+ }
+
+ sb.Append ("</RSAKeyValue>");
+ }
+ catch {
+ if (rsaParams.P != null)
+ Array.Clear (rsaParams.P, 0, rsaParams.P.Length);
+ if (rsaParams.Q != null)
+ Array.Clear (rsaParams.Q, 0, rsaParams.Q.Length);
+ if (rsaParams.DP != null)
+ Array.Clear (rsaParams.DP, 0, rsaParams.DP.Length);
+ if (rsaParams.DQ != null)
+ Array.Clear (rsaParams.DQ, 0, rsaParams.DQ.Length);
+ if (rsaParams.InverseQ != null)
+ Array.Clear (rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
+ if (rsaParams.D != null)
+ Array.Clear (rsaParams.D, 0, rsaParams.D.Length);
+ throw;
+ }
+
+ return sb.ToString ();
+ }
+
+ // internal for Mono 1.0.x in order to preserve public contract
+ // they are public for Mono 1.1.x (for 1.2) as the API isn't froze ATM
+
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ bool UseKeyBlinding {
+ get { return keyBlinding; }
+ // you REALLY shoudn't touch this (true is fine ;-)
+ set { keyBlinding = value; }
+ }
+
+#if NET_2_0
+ public
+#else
+ internal
+#endif
+ bool IsCrtPossible {
+ // either the key pair isn't generated (and will be
+ // generated with CRT parameters) or CRT is (or isn't)
+ // 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;
+ }
}
}