//
// RSACryptoServiceProvider.cs: Handles an RSA implementation.
//
-// Author:
-// Sebastien Pouliot (spouliot@motus.com)
+// Authors:
+// Sebastien Pouliot <sebastien@ximian.com>
+// Ben Maurer (bmaurer@users.sf.net)
//
-// (C) 2002 Motus Technologies Inc. (http://www.motus.com)
-// Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
-// See bouncycastle.txt for license.
+// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
+// Portions (C) 2003 Ben Maurer
+// (C) 2004 Novell (http://www.novell.com)
//
using System;
public sealed class RSACryptoServiceProvider : RSA {
- private CspParameters cspParams;
+ 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 keypairGenerated = false;
- private bool persistKey = false;
- private bool m_disposed = false;
-
- private BigInteger d;
- private BigInteger p;
- private BigInteger q;
- private BigInteger dp;
- private BigInteger dq;
- private BigInteger qInv;
- private BigInteger n; // modulus
- private BigInteger e;
+ private bool m_disposed;
+
+ private RSAManaged rsa;
public RSACryptoServiceProvider ()
{
// So we'll generate the keypair only when (and if) it's being
// used (or exported). This should save us a lot of time (at
// least in the unit tests).
- Common (null);
+ Common (1024, null);
}
public RSACryptoServiceProvider (CspParameters parameters)
{
- Common (parameters);
+ Common (1024, parameters);
// no keypair generation done at this stage
}
public RSACryptoServiceProvider (int dwKeySize)
{
// Here it's clear that we need to generate a new keypair
- Common (null);
- GenerateKeyPair (dwKeySize);
+ Common (dwKeySize, null);
+ // 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 ?
public RSACryptoServiceProvider (int dwKeySize, CspParameters parameters)
{
- Common (parameters);
- GenerateKeyPair (dwKeySize);
+ Common (dwKeySize, parameters);
+ // no keypair generation done at this stage
}
- [MonoTODO("Persistance")]
- // FIXME: We currently dont link with MS CAPI. Anyway this makes
- // only sense in Windows - what do we do elsewhere ?
- private void Common (CspParameters p)
+ private void Common (int dwKeySize, CspParameters p)
{
- cspParams = new CspParameters ();
- if (p == null) {
- // TODO: set default values (for keypair persistance)
- }
- else {
- cspParams = p;
- // FIXME: We'll need this to support some kind of persistance
- throw new NotSupportedException ("CspParameters not supported");
- }
// Microsoft RSA CSP can do between 384 and 16384 bits keypair
- // we limit ourselve to 2048 because (a) BigInteger limits and (b) it's so SLOW
LegalKeySizesValue = new KeySizes [1];
- LegalKeySizesValue [0] = new KeySizes (384, 2048, 8);
- }
-
- private void GenerateKeyPair (int dwKeySize)
- {
- // will throw an exception is key size isn't supported
+ LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
base.KeySize = dwKeySize;
-
- // p and q values should have a length of half the strength in bits
- int pbitlength = ((dwKeySize + 1) >> 1);
- int qbitlength = (dwKeySize - pbitlength);
- e = new BigInteger (17); // fixed
-
- // generate p, prime and (p-1) relatively prime to e
- for (;;) {
- p = BigInteger.genPseudoPrime (pbitlength, 80);
- if (e.gcd (p - 1) == 1)
- break;
+
+ 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_0
+ if (useMachineKeyStore)
+ p.Flags |= CspProviderFlags.UseMachineKeyStore;
+#endif
+ store = new KeyPairPersistence (p);
+ // no need to load - it cannot exists
}
- // generate a modulus of the required length
- for (;;) {
- // generate q, prime and (q-1) relatively prime to e,
- // and not equal to p
- for (;;) {
- q = BigInteger.genPseudoPrime (qbitlength, 80);
- if ((e.gcd (q - 1) == 1) && (p != q))
- break;
+ else {
+ store = new KeyPairPersistence (p);
+ store.Load ();
+ if (store.KeyValue != null) {
+ persisted = true;
+ this.FromXmlString (store.KeyValue);
}
-
- // calculate the modulus
- n = p * q;
- if (n.bitCount () == dwKeySize)
- break;
-
- // if we get here our primes aren't big enough, make the largest
- // of the two p and try again
- p = p.max (q);
}
-
- BigInteger pSub1 = (p - 1);
- BigInteger qSub1 = (q - 1);
- BigInteger phi = pSub1 * qSub1;
-
- // calculate the private exponent
- d = e.modInverse (phi);
-
- // calculate the CRT factors
- dp = d % pSub1;
- dq = d % qSub1;
- qInv = q.modInverse (p);
-
- keypairGenerated = true;
}
+
+#if ! NET_1_0
+ private static bool useMachineKeyStore = false;
+
+ public static bool UseMachineKeyStore {
+ get { return useMachineKeyStore; }
+ set { useMachineKeyStore = value; }
+ }
+#endif
- // Zeroize private key
- ~RSACryptoServiceProvider()
+ ~RSACryptoServiceProvider ()
{
+ // Zeroize private key
Dispose (false);
}
}
public override int KeySize {
- get { return n.bitCount(); }
+ get {
+ if (rsa == null)
+ return KeySizeValue;
+ else
+ return rsa.KeySize;
+ }
}
-
- [MonoTODO("Persistance")]
+
public bool PersistKeyInCsp {
- get { return false; }
- set { throw new NotSupportedException (); }
+ get { return persistKey; }
+ set {
+ persistKey = value;
+ if (persistKey)
+ OnKeyGenerated (rsa);
+ }
+ }
+
+#if (NET_1_0 || NET_1_1)
+ internal
+#else
+ public
+#endif
+ bool PublicOnly {
+ get { return rsa.PublicOnly; }
}
public override string SignatureAlgorithm {
public byte[] Decrypt (byte[] rgb, bool fOAEP)
{
+#if NET_1_1
+ if (m_disposed)
+ throw new ObjectDisposedException ("rsa");
+#endif
// choose between OAEP or PKCS#1 v.1.5 padding
- if (fOAEP) {
- SHA1 sha1 = SHA1.Create ();
- return PKCS1.Decrypt_OAEP (this, sha1, null);
- }
- else {
- return PKCS1.Decrypt_v15 (this, rgb);
- }
+ AsymmetricKeyExchangeDeformatter def = null;
+ if (fOAEP)
+ def = new RSAOAEPKeyExchangeDeformatter (rsa);
+ else
+ def = new RSAPKCS1KeyExchangeDeformatter (rsa);
+
+ return def.DecryptKeyExchange (rgb);
}
// NOTE: Unlike MS we need this method
// Using this method to decrypt data IS dangerous (and very slow).
public override byte[] DecryptValue (byte[] rgb)
{
- // it would be stupid to decrypt data with a newly
- // generated keypair - so we return false
- if (!keypairGenerated)
- return null;
-
- BigInteger input = new BigInteger (rgb);
- BigInteger output = input.modPow (d, n);
- return output.getBytes ();
+ return rsa.DecryptValue (rgb);
}
public byte[] Encrypt (byte[] rgb, bool fOAEP)
{
- RandomNumberGenerator rng = RandomNumberGenerator.Create ();
// choose between OAEP or PKCS#1 v.1.5 padding
- if (fOAEP) {
- SHA1 sha1 = SHA1.Create ();
- return PKCS1.Encrypt_OAEP (this, sha1, rng, rgb);
- }
- else {
- return PKCS1.Encrypt_v15 (this, rng, rgb) ;
- }
+ AsymmetricKeyExchangeFormatter fmt = null;
+ if (fOAEP)
+ fmt = new RSAOAEPKeyExchangeFormatter (rsa);
+ else
+ fmt = new RSAPKCS1KeyExchangeFormatter (rsa);
+
+ return fmt.CreateKeyExchange (rgb);
}
// NOTE: Unlike MS we need this method
// Using this method to encrypt data IS dangerous (and very slow).
public override byte[] EncryptValue (byte[] rgb)
{
- if (!keypairGenerated)
- GenerateKeyPair (1024);
-
- // TODO: With CRT
- // without CRT
- BigInteger input = new BigInteger (rgb);
- BigInteger output = input.modPow (e, n);
- return output.getBytes ();
+ return rsa.EncryptValue (rgb);
}
public override RSAParameters ExportParameters (bool includePrivateParameters)
{
if ((includePrivateParameters) && (!privateKeyExportable))
throw new CryptographicException ("cannot export private key");
- if (!keypairGenerated)
- GenerateKeyPair (1024);
-
- RSAParameters param = new RSAParameters();
- param.Exponent = e.getBytes ();
- param.Modulus = n.getBytes ();
- if (includePrivateParameters) {
- param.D = d.getBytes ();
- param.DP = dp.getBytes ();
- param.DQ = dq.getBytes ();
- param.InverseQ = qInv.getBytes ();
- param.P = p.getBytes ();
- param.Q = q.getBytes ();
- }
- return param;
+
+ return rsa.ExportParameters (includePrivateParameters);
}
public override void ImportParameters (RSAParameters parameters)
{
- // if missing "mandatory" parameters
- if ((parameters.Exponent == null) || (parameters.Modulus == null))
- throw new CryptographicException ();
-
- e = new BigInteger (parameters.Exponent);
- n = new BigInteger (parameters.Modulus);
- // only if the private key is present
- if (parameters.D != null)
- d = new BigInteger (parameters.D);
- if (parameters.DP != null)
- dp = new BigInteger (parameters.DP);
- if (parameters.DQ != null)
- dq = new BigInteger (parameters.DQ);
- if (parameters.InverseQ != null)
- qInv = new BigInteger (parameters.InverseQ);
- if (parameters.P != null)
- p = new BigInteger (parameters.P);
- if (parameters.Q != null)
- q = new BigInteger (parameters.Q);
-
- // we now have a keypair
- keypairGenerated = true;
+ rsa.ImportParameters (parameters);
}
private HashAlgorithm GetHash (object halg)
{
if (halg == null)
- throw new ArgumentNullException ();
-
+ throw new ArgumentNullException ("halg");
+
HashAlgorithm hash = null;
if (halg is String)
hash = HashAlgorithm.Create ((String)halg);
else if (halg is Type)
hash = (HashAlgorithm) Activator.CreateInstance ((Type)halg);
else
- throw new ArgumentException ();
-
+ throw new ArgumentException ("halg");
+
return hash;
}
+ // NOTE: this method can work with ANY configured (OID in machine.config)
+ // HashAlgorithm descendant
public byte[] SignData (byte[] buffer, object halg)
{
+#if NET_1_1
+ if (buffer == null)
+ throw new ArgumentNullException ("buffer");
+#endif
return SignData (buffer, 0, buffer.Length, halg);
}
+ // NOTE: this method can work with ANY configured (OID in machine.config)
+ // HashAlgorithm descendant
public byte[] SignData (Stream inputStream, object halg)
{
HashAlgorithm hash = GetHash (halg);
byte[] toBeSigned = hash.ComputeHash (inputStream);
-
- string oid = CryptoConfig.MapNameToOID (hash.ToString ());
- return SignHash (toBeSigned, oid);
+ return PKCS1.Sign_v15 (this, hash, toBeSigned);
}
+ // NOTE: this method can work with ANY configured (OID in machine.config)
+ // HashAlgorithm descendant
public byte[] SignData (byte[] buffer, int offset, int count, object halg)
{
HashAlgorithm hash = GetHash (halg);
byte[] toBeSigned = hash.ComputeHash (buffer, offset, count);
- string oid = CryptoConfig.MapNameToOID (hash.ToString ());
- return SignHash (toBeSigned, oid);
+ return PKCS1.Sign_v15 (this, hash, toBeSigned);
}
- private void ValidateHash (string oid, int length)
+ private string GetHashNameFromOID (string oid)
{
- if (oid == "1.3.14.3.2.26") {
- if (length != 20)
- throw new CryptographicException ("wrong hash size for SHA1");
- }
- else if (oid == "1.2.840.113549.2.5") {
- if (length != 16)
- throw new CryptographicException ("wrong hash size for MD5");
+ switch (oid) {
+ case "1.3.14.3.2.26":
+ return "SHA1";
+ case "1.2.840.113549.2.5":
+ return "MD5";
+ default:
+ throw new NotSupportedException (oid + " is an unsupported hash algorithm for RSA signing");
}
- else
- throw new NotSupportedException (oid + " is an unsupported hash algorithm for RSA signing");
}
-
+
+ // 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.
+ // 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 ();
+ throw new ArgumentNullException ("rgbHash");
- if (!keypairGenerated)
- GenerateKeyPair (1024);
-
- ValidateHash (str, rgbHash.Length);
-
- return PKCS1.Sign_v15 (this, str, rgbHash);
+ HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
+ return PKCS1.Sign_v15 (this, hash, rgbHash);
}
-
+
+ // NOTE: this method can work with ANY configured (OID in machine.config)
+ // 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");
+
HashAlgorithm hash = GetHash (halg);
byte[] toBeVerified = hash.ComputeHash (buffer);
- string oid = CryptoConfig.MapNameToOID (hash.ToString ());
- return VerifyHash (toBeVerified, oid, signature);
+ return PKCS1.Verify_v15 (this, hash, toBeVerified, signature);
}
+ // 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 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)
{
- if ((rgbHash == null) || (rgbSignature == null))
- throw new ArgumentNullException ();
-
- // it would be stupid to verify a signature with a newly
- // generated keypair - so we return false
- if (!keypairGenerated)
- return false;
+ if (rgbHash == null)
+ throw new ArgumentNullException ("rgbHash");
+ if (rgbSignature == null)
+ throw new ArgumentNullException ("rgbSignature");
- ValidateHash (str, rgbHash.Length);
-
- return PKCS1.Verify_v15 (this, str, rgbHash, rgbSignature);
+ HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
+ return PKCS1.Verify_v15 (this, hash, rgbHash, rgbSignature);
}
- [MonoTODO()]
protected override void Dispose (bool disposing)
{
if (!m_disposed) {
- // TODO: always zeroize private key
- if(disposing) {
- // TODO: Dispose managed resources
+ // the key is persisted and we do not want it persisted
+ if ((persisted) && (!persistKey)) {
+ store.Remove (); // delete the container
}
-
- // TODO: Dispose unmanaged resources
+ 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)
+ {
+ // 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;
}
- // call base class
- // no need as they all are abstract before us
- m_disposed = true;
}
}
}