summary refs log tree commit diff
path: root/Crypto/src/crypto/signers
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src/crypto/signers')
-rw-r--r--Crypto/src/crypto/signers/DsaDigestSigner.cs145
-rw-r--r--Crypto/src/crypto/signers/DsaSigner.cs136
-rw-r--r--Crypto/src/crypto/signers/ECDsaSigner.cs156
-rw-r--r--Crypto/src/crypto/signers/ECGOST3410Signer.cs154
-rw-r--r--Crypto/src/crypto/signers/ECNRSigner.cs186
-rw-r--r--Crypto/src/crypto/signers/GOST3410DigestSigner.cs145
-rw-r--r--Crypto/src/crypto/signers/GOST3410Signer.cs132
-rw-r--r--Crypto/src/crypto/signers/GenericSigner.cs129
-rw-r--r--Crypto/src/crypto/signers/Iso9796d2PssSigner.cs576
-rw-r--r--Crypto/src/crypto/signers/Iso9796d2Signer.cs557
-rw-r--r--Crypto/src/crypto/signers/PssSigner.cs345
-rw-r--r--Crypto/src/crypto/signers/RsaDigestSigner.cs228
12 files changed, 2889 insertions, 0 deletions
diff --git a/Crypto/src/crypto/signers/DsaDigestSigner.cs b/Crypto/src/crypto/signers/DsaDigestSigner.cs
new file mode 100644
index 000000000..aee713450
--- /dev/null
+++ b/Crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	public class DsaDigestSigner
+		: ISigner
+	{
+		private readonly IDigest digest;
+		private readonly IDsa dsaSigner;
+		private bool forSigning;
+
+		public DsaDigestSigner(
+			IDsa	signer,
+			IDigest	digest)
+		{
+			this.digest = digest;
+			this.dsaSigner = signer;
+		}
+
+		public string AlgorithmName
+		{
+			get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
+		}
+
+		public void Init(
+			bool							forSigning,
+			ICipherParameters	parameters)
+		{
+			this.forSigning = forSigning;
+
+			AsymmetricKeyParameter k;
+
+			if (parameters is ParametersWithRandom)
+			{
+				k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+			}
+			else
+			{
+				k = (AsymmetricKeyParameter)parameters;
+			}
+
+			if (forSigning && !k.IsPrivate)
+				throw new InvalidKeyException("Signing Requires Private Key.");
+
+			if (!forSigning && k.IsPrivate)
+				throw new InvalidKeyException("Verification Requires Public Key.");
+
+			Reset();
+
+			dsaSigner.Init(forSigning, parameters);
+		}
+
+		/**
+		 * update the internal digest with the byte b
+		 */
+		public void Update(
+			byte input)
+		{
+			digest.Update(input);
+		}
+
+		/**
+		 * update the internal digest with the byte array in
+		 */
+		public void BlockUpdate(
+			byte[]	input,
+			int			inOff,
+			int			length)
+		{
+			digest.BlockUpdate(input, inOff, length);
+		}
+
+		/**
+		 * Generate a signature for the message we've been loaded with using
+		 * the key we were initialised with.
+     */
+		public byte[] GenerateSignature()
+		{
+			if (!forSigning)
+				throw new InvalidOperationException("DSADigestSigner not initialised for signature generation.");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			BigInteger[] sig = dsaSigner.GenerateSignature(hash);
+
+			return DerEncode(sig[0], sig[1]);
+		}
+
+		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
+		public bool VerifySignature(
+			byte[] signature)
+		{
+			if (forSigning)
+				throw new InvalidOperationException("DSADigestSigner not initialised for verification");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			try
+			{
+				BigInteger[] sig = DerDecode(signature);
+				return dsaSigner.VerifySignature(hash, sig[0], sig[1]);
+			}
+			catch (IOException)
+			{
+				return false;
+			}
+		}
+
+		/// <summary>Reset the internal state</summary>
+		public void Reset()
+		{
+			digest.Reset();
+		}
+
+		private byte[] DerEncode(
+			BigInteger	r,
+			BigInteger	s)
+		{
+			return new DerSequence(new DerInteger(r), new DerInteger(s)).GetDerEncoded();
+		}
+
+		private BigInteger[] DerDecode(
+			byte[] encoding)
+		{
+			Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding);
+
+			return new BigInteger[]
+			{
+				((DerInteger) s[0]).Value,
+				((DerInteger) s[1]).Value
+			};
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/DsaSigner.cs b/Crypto/src/crypto/signers/DsaSigner.cs
new file mode 100644
index 000000000..419b1972e
--- /dev/null
+++ b/Crypto/src/crypto/signers/DsaSigner.cs
@@ -0,0 +1,136 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/**
+	 * The Digital Signature Algorithm - as described in "Handbook of Applied
+	 * Cryptography", pages 452 - 453.
+	 */
+	public class DsaSigner
+		: IDsa
+	{
+		private DsaKeyParameters key;
+		private SecureRandom random;
+
+		public string AlgorithmName
+		{
+			get { return "DSA"; }
+		}
+
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			if (forSigning)
+			{
+				if (parameters is ParametersWithRandom)
+				{
+					ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+					this.random = rParam.Random;
+					parameters = rParam.Parameters;
+				}
+				else
+				{
+					this.random = new SecureRandom();
+				}
+
+				if (!(parameters is DsaPrivateKeyParameters))
+					throw new InvalidKeyException("DSA private key required for signing");
+
+				this.key = (DsaPrivateKeyParameters) parameters;
+			}
+			else
+			{
+				if (!(parameters is DsaPublicKeyParameters))
+					throw new InvalidKeyException("DSA public key required for verification");
+
+				this.key = (DsaPublicKeyParameters) parameters;
+			}
+		}
+
+		/**
+		 * Generate a signature for the given message using the key we were
+		 * initialised with. For conventional DSA the message should be a SHA-1
+		 * hash of the message of interest.
+		 *
+		 * @param message the message that will be verified later.
+		 */
+		public BigInteger[] GenerateSignature(
+			byte[] message)
+		{
+			DsaParameters parameters = key.Parameters;
+			BigInteger q = parameters.Q;
+			BigInteger m = calculateE(q, message);
+			BigInteger k;
+
+			do
+			{
+				k = new BigInteger(q.BitLength, random);
+			}
+			while (k.CompareTo(q) >= 0);
+
+			BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q);
+
+			k = k.ModInverse(q).Multiply(
+				m.Add(((DsaPrivateKeyParameters)key).X.Multiply(r)));
+
+			BigInteger s = k.Mod(q);
+
+			return new BigInteger[]{ r, s };
+		}
+
+		/**
+		 * return true if the value r and s represent a DSA signature for
+		 * the passed in message for standard DSA the message should be a
+		 * SHA-1 hash of the real message to be verified.
+		 */
+		public bool VerifySignature(
+			byte[]		message,
+			BigInteger	r,
+			BigInteger	s)
+		{
+			DsaParameters parameters = key.Parameters;
+			BigInteger q = parameters.Q;
+			BigInteger m = calculateE(q, message);
+
+			if (r.SignValue <= 0 || q.CompareTo(r) <= 0)
+			{
+				return false;
+			}
+
+			if (s.SignValue <= 0 || q.CompareTo(s) <= 0)
+			{
+				return false;
+			}
+
+			BigInteger w = s.ModInverse(q);
+
+			BigInteger u1 = m.Multiply(w).Mod(q);
+			BigInteger u2 = r.Multiply(w).Mod(q);
+
+			BigInteger p = parameters.P;
+			u1 = parameters.G.ModPow(u1, p);
+			u2 = ((DsaPublicKeyParameters)key).Y.ModPow(u2, p);
+
+			BigInteger v = u1.Multiply(u2).Mod(p).Mod(q);
+
+			return v.Equals(r);
+		}
+
+		private BigInteger calculateE(
+			BigInteger	n,
+			byte[]		message)
+		{
+			int length = System.Math.Min(message.Length, n.BitLength / 8);
+
+			return new BigInteger(1, message, 0, length);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/ECDsaSigner.cs b/Crypto/src/crypto/signers/ECDsaSigner.cs
new file mode 100644
index 000000000..4254e5590
--- /dev/null
+++ b/Crypto/src/crypto/signers/ECDsaSigner.cs
@@ -0,0 +1,156 @@
+using System;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/**
+	 * EC-DSA as described in X9.62
+	 */
+	public class ECDsaSigner
+		: IDsa
+	{
+		private ECKeyParameters key;
+		private SecureRandom random;
+
+		public string AlgorithmName
+		{
+			get { return "ECDSA"; }
+		}
+
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			if (forSigning)
+			{
+				if (parameters is ParametersWithRandom)
+				{
+					ParametersWithRandom rParam = (ParametersWithRandom) parameters;
+
+					this.random = rParam.Random;
+					parameters = rParam.Parameters;
+				}
+				else
+				{
+					this.random = new SecureRandom();
+				}
+
+				if (!(parameters is ECPrivateKeyParameters))
+					throw new InvalidKeyException("EC private key required for signing");
+
+				this.key = (ECPrivateKeyParameters) parameters;
+			}
+			else
+			{
+				if (!(parameters is ECPublicKeyParameters))
+					throw new InvalidKeyException("EC public key required for verification");
+
+				this.key = (ECPublicKeyParameters) parameters;
+			}
+		}
+
+		// 5.3 pg 28
+		/**
+		 * Generate a signature for the given message using the key we were
+		 * initialised with. For conventional DSA the message should be a SHA-1
+		 * hash of the message of interest.
+		 *
+		 * @param message the message that will be verified later.
+		 */
+		public BigInteger[] GenerateSignature(
+			byte[] message)
+		{
+			BigInteger n = key.Parameters.N;
+			BigInteger e = calculateE(n, message);
+
+			BigInteger r = null;
+			BigInteger s = null;
+
+			// 5.3.2
+			do // Generate s
+			{
+				BigInteger k = null;
+
+				do // Generate r
+				{
+					do
+					{
+						k = new BigInteger(n.BitLength, random);
+					}
+					while (k.SignValue == 0 || k.CompareTo(n) >= 0);
+
+					ECPoint p = key.Parameters.G.Multiply(k);
+
+					// 5.3.3
+					BigInteger x = p.X.ToBigInteger();
+
+					r = x.Mod(n);
+				}
+				while (r.SignValue == 0);
+
+				BigInteger d = ((ECPrivateKeyParameters)key).D;
+
+				s = k.ModInverse(n).Multiply(e.Add(d.Multiply(r).Mod(n))).Mod(n);
+			}
+			while (s.SignValue == 0);
+
+			return new BigInteger[]{ r, s };
+		}
+
+		// 5.4 pg 29
+		/**
+		 * return true if the value r and s represent a DSA signature for
+		 * the passed in message (for standard DSA the message should be
+		 * a SHA-1 hash of the real message to be verified).
+		 */
+		public bool VerifySignature(
+			byte[]		message,
+			BigInteger	r,
+			BigInteger	s)
+		{
+			BigInteger n = key.Parameters.N;
+
+			// r and s should both in the range [1,n-1]
+			if (r.SignValue < 1 || s.SignValue < 1
+				|| r.CompareTo(n) >= 0 || s.CompareTo(n) >= 0)
+			{
+				return false;
+			}
+
+			BigInteger e = calculateE(n, message);
+			BigInteger c = s.ModInverse(n);
+
+			BigInteger u1 = e.Multiply(c).Mod(n);
+			BigInteger u2 = r.Multiply(c).Mod(n);
+
+			ECPoint G = key.Parameters.G;
+			ECPoint Q = ((ECPublicKeyParameters) key).Q;
+
+			ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, u1, Q, u2);
+
+			BigInteger v = point.X.ToBigInteger().Mod(n);
+
+			return v.Equals(r);
+		}
+
+		private BigInteger calculateE(
+			BigInteger	n,
+			byte[]		message)
+		{
+			int messageBitLength = message.Length * 8;
+			BigInteger trunc = new BigInteger(1, message);
+
+			if (n.BitLength < messageBitLength)
+			{
+				trunc = trunc.ShiftRight(messageBitLength - n.BitLength);
+			}
+
+			return trunc;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/ECGOST3410Signer.cs b/Crypto/src/crypto/signers/ECGOST3410Signer.cs
new file mode 100644
index 000000000..d68b83f67
--- /dev/null
+++ b/Crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -0,0 +1,154 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/**
+	 * GOST R 34.10-2001 Signature Algorithm
+	 */
+	public class ECGost3410Signer
+		: IDsa
+	{
+		private ECKeyParameters key;
+		private SecureRandom random;
+
+		public string AlgorithmName
+		{
+			get { return "ECGOST3410"; }
+		}
+
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			if (forSigning)
+			{
+				if (parameters is ParametersWithRandom)
+				{
+					ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+					this.random = rParam.Random;
+					parameters = rParam.Parameters;
+				}
+				else
+				{
+					this.random = new SecureRandom();
+				}
+
+				if (!(parameters is ECPrivateKeyParameters))
+					throw new InvalidKeyException("EC private key required for signing");
+
+				this.key = (ECPrivateKeyParameters) parameters;
+			}
+			else
+			{
+				if (!(parameters is ECPublicKeyParameters))
+					throw new InvalidKeyException("EC public key required for verification");
+
+				this.key = (ECPublicKeyParameters)parameters;
+			}
+		}
+
+		/**
+		 * generate a signature for the given message using the key we were
+		 * initialised with. For conventional GOST3410 the message should be a GOST3411
+		 * hash of the message of interest.
+		 *
+		 * @param message the message that will be verified later.
+		 */
+		public BigInteger[] GenerateSignature(
+			byte[] message)
+		{
+			byte[] mRev = new byte[message.Length]; // conversion is little-endian
+			for (int i = 0; i != mRev.Length; i++)
+			{
+				mRev[i] = message[mRev.Length - 1 - i];
+			}
+
+			BigInteger e = new BigInteger(1, mRev);
+			BigInteger n = key.Parameters.N;
+
+			BigInteger r = null;
+			BigInteger s = null;
+
+			do // generate s
+			{
+				BigInteger k = null;
+
+				do // generate r
+				{
+					do
+					{
+						k = new BigInteger(n.BitLength, random);
+					}
+					while (k.SignValue == 0);
+
+					ECPoint p = key.Parameters.G.Multiply(k);
+
+					BigInteger x = p.X.ToBigInteger();
+
+					r = x.Mod(n);
+				}
+				while (r.SignValue == 0);
+
+				BigInteger d = ((ECPrivateKeyParameters)key).D;
+
+				s = (k.Multiply(e)).Add(d.Multiply(r)).Mod(n);
+			}
+			while (s.SignValue == 0);
+
+			return new BigInteger[]{ r, s };
+		}
+
+		/**
+		 * return true if the value r and s represent a GOST3410 signature for
+		 * the passed in message (for standard GOST3410 the message should be
+		 * a GOST3411 hash of the real message to be verified).
+		 */
+		public bool VerifySignature(
+			byte[]		message,
+			BigInteger	r,
+			BigInteger	s)
+		{
+			byte[] mRev = new byte[message.Length]; // conversion is little-endian
+			for (int i = 0; i != mRev.Length; i++)
+			{
+				mRev[i] = message[mRev.Length - 1 - i];
+			}
+
+			BigInteger e = new BigInteger(1, mRev);
+			BigInteger n = key.Parameters.N;
+
+			// r in the range [1,n-1]
+			if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+			{
+				return false;
+			}
+
+			// s in the range [1,n-1]
+			if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
+			{
+				return false;
+			}
+
+			BigInteger v = e.ModInverse(n);
+
+			BigInteger z1 = s.Multiply(v).Mod(n);
+			BigInteger z2 = (n.Subtract(r)).Multiply(v).Mod(n);
+
+			ECPoint G = key.Parameters.G; // P
+			ECPoint Q = ((ECPublicKeyParameters)key).Q;
+
+			ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, z1, Q, z2);
+
+			BigInteger R = point.X.ToBigInteger().Mod(n);
+
+			return R.Equals(r);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/ECNRSigner.cs b/Crypto/src/crypto/signers/ECNRSigner.cs
new file mode 100644
index 000000000..63865d731
--- /dev/null
+++ b/Crypto/src/crypto/signers/ECNRSigner.cs
@@ -0,0 +1,186 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/**
+	 * EC-NR as described in IEEE 1363-2000
+	 */
+	public class ECNRSigner
+		: IDsa
+	{
+		private bool			forSigning;
+		private ECKeyParameters	key;
+		private SecureRandom	random;
+
+		public string AlgorithmName
+		{
+			get { return "ECNR"; }
+		}
+
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			this.forSigning = forSigning;
+
+			if (forSigning)
+			{
+				if (parameters is ParametersWithRandom)
+				{
+					ParametersWithRandom rParam = (ParametersWithRandom) parameters;
+
+					this.random = rParam.Random;
+					parameters = rParam.Parameters;
+				}
+				else
+				{
+					this.random = new SecureRandom();
+				}
+
+				if (!(parameters is ECPrivateKeyParameters))
+					throw new InvalidKeyException("EC private key required for signing");
+
+				this.key = (ECPrivateKeyParameters) parameters;
+			}
+			else
+			{
+				if (!(parameters is ECPublicKeyParameters))
+					throw new InvalidKeyException("EC public key required for verification");
+
+				this.key = (ECPublicKeyParameters) parameters;
+			}
+		}
+
+		// Section 7.2.5 ECSP-NR, pg 34
+		/**
+		 * generate a signature for the given message using the key we were
+		 * initialised with.  Generally, the order of the curve should be at
+		 * least as long as the hash of the message of interest, and with
+		 * ECNR it *must* be at least as long.
+		 *
+		 * @param digest  the digest to be signed.
+		 * @exception DataLengthException if the digest is longer than the key allows
+		 */
+		public BigInteger[] GenerateSignature(
+			byte[] message)
+		{
+			if (!this.forSigning)
+			{
+				// not properly initilaized... deal with it
+				throw new InvalidOperationException("not initialised for signing");
+			}
+
+			BigInteger n = ((ECPrivateKeyParameters) this.key).Parameters.N;
+			int nBitLength = n.BitLength;
+
+			BigInteger e = new BigInteger(1, message);
+			int eBitLength = e.BitLength;
+
+			ECPrivateKeyParameters  privKey = (ECPrivateKeyParameters)key;
+
+			if (eBitLength > nBitLength)
+			{
+				throw new DataLengthException("input too large for ECNR key.");
+			}
+
+			BigInteger r = null;
+			BigInteger s = null;
+
+			AsymmetricCipherKeyPair tempPair;
+			do // generate r
+			{
+				// generate another, but very temporary, key pair using
+				// the same EC parameters
+				ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
+
+				keyGen.Init(new ECKeyGenerationParameters(privKey.Parameters, this.random));
+
+				tempPair = keyGen.GenerateKeyPair();
+
+				//    BigInteger Vx = tempPair.getPublic().getW().getAffineX();
+				ECPublicKeyParameters V = (ECPublicKeyParameters) tempPair.Public; // get temp's public key
+				BigInteger Vx = V.Q.X.ToBigInteger(); // get the point's x coordinate
+
+				r = Vx.Add(e).Mod(n);
+			}
+			while (r.SignValue == 0);
+
+			// generate s
+			BigInteger x = privKey.D;                // private key value
+			BigInteger u = ((ECPrivateKeyParameters) tempPair.Private).D; // temp's private key value
+			s = u.Subtract(r.Multiply(x)).Mod(n);
+
+			return new BigInteger[]{ r, s };
+		}
+
+		// Section 7.2.6 ECVP-NR, pg 35
+		/**
+		 * return true if the value r and s represent a signature for the
+		 * message passed in. Generally, the order of the curve should be at
+		 * least as long as the hash of the message of interest, and with
+		 * ECNR, it *must* be at least as long.  But just in case the signer
+		 * applied mod(n) to the longer digest, this implementation will
+		 * apply mod(n) during verification.
+		 *
+		 * @param digest  the digest to be verified.
+		 * @param r       the r value of the signature.
+		 * @param s       the s value of the signature.
+		 * @exception DataLengthException if the digest is longer than the key allows
+		 */
+		public bool VerifySignature(
+			byte[]		message,
+			BigInteger	r,
+			BigInteger	s)
+		{
+			if (this.forSigning)
+			{
+				// not properly initilaized... deal with it
+				throw new InvalidOperationException("not initialised for verifying");
+			}
+
+			ECPublicKeyParameters pubKey = (ECPublicKeyParameters)key;
+			BigInteger n = pubKey.Parameters.N;
+			int nBitLength = n.BitLength;
+
+			BigInteger e = new BigInteger(1, message);
+			int eBitLength = e.BitLength;
+
+			if (eBitLength > nBitLength)
+			{
+				throw new DataLengthException("input too large for ECNR key.");
+			}
+
+			// r in the range [1,n-1]
+			if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+			{
+				return false;
+			}
+
+			// TODO So why is this different from the spec?
+			// s in the range [0,n-1]           NB: ECNR spec says 0
+			if (s.CompareTo(BigInteger.Zero) < 0 || s.CompareTo(n) >= 0)
+			{
+				return false;
+			}
+
+			// compute P = sG + rW
+
+			ECPoint G = pubKey.Parameters.G;
+			ECPoint W = pubKey.Q;
+			// calculate P using Bouncy math
+			ECPoint P = ECAlgorithms.SumOfTwoMultiplies(G, s, W, r);
+
+			BigInteger x = P.X.ToBigInteger();
+			BigInteger t = r.Subtract(x).Mod(n);
+
+			return t.Equals(e);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/GOST3410DigestSigner.cs b/Crypto/src/crypto/signers/GOST3410DigestSigner.cs
new file mode 100644
index 000000000..58aefa368
--- /dev/null
+++ b/Crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	public class Gost3410DigestSigner
+		: ISigner
+	{
+		private readonly IDigest digest;
+		private readonly IDsa dsaSigner;
+		private bool forSigning;
+
+		public Gost3410DigestSigner(
+			IDsa	signer,
+			IDigest	digest)
+		{
+			this.dsaSigner = signer;
+			this.digest = digest;
+		}
+
+		public string AlgorithmName
+		{
+			get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
+		}
+
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			this.forSigning = forSigning;
+
+			AsymmetricKeyParameter k;
+			if (parameters is ParametersWithRandom)
+			{
+				k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+			}
+			else
+			{
+				k = (AsymmetricKeyParameter)parameters;
+			}
+
+			if (forSigning && !k.IsPrivate)
+			{
+				throw new InvalidKeyException("Signing Requires Private Key.");
+			}
+
+			if (!forSigning && k.IsPrivate)
+			{
+				throw new InvalidKeyException("Verification Requires Public Key.");
+			}
+
+			Reset();
+
+			dsaSigner.Init(forSigning, parameters);
+		}
+
+		/**
+		 * update the internal digest with the byte b
+		 */
+		public void Update(
+			byte input)
+		{
+			digest.Update(input);
+		}
+
+		/**
+		 * update the internal digest with the byte array in
+		 */
+		public void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		length)
+		{
+			digest.BlockUpdate(input, inOff, length);
+		}
+
+		/**
+		 * Generate a signature for the message we've been loaded with using
+		 * the key we were initialised with.
+		 */
+		public byte[] GenerateSignature()
+		{
+			if (!forSigning)
+				throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation.");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			try
+			{
+				BigInteger[] sig = dsaSigner.GenerateSignature(hash);
+				byte[] sigBytes = new byte[64];
+
+				// TODO Add methods to allow writing BigInteger to existing byte array?
+				byte[] r = sig[0].ToByteArrayUnsigned();
+				byte[] s = sig[1].ToByteArrayUnsigned();
+				s.CopyTo(sigBytes, 32 - s.Length);
+				r.CopyTo(sigBytes, 64 - r.Length);
+				return sigBytes;
+			}
+			catch (Exception e)
+			{
+				throw new SignatureException(e.Message, e);
+			}
+		}
+
+		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
+		public bool VerifySignature(
+			byte[] signature)
+		{
+			if (forSigning)
+				throw new InvalidOperationException("DSADigestSigner not initialised for verification");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			BigInteger R, S;
+			try
+			{
+				R = new BigInteger(1, signature, 32, 32);
+				S = new BigInteger(1, signature, 0, 32);
+			}
+			catch (Exception e)
+			{
+				throw new SignatureException("error decoding signature bytes.", e);
+			}
+
+			return dsaSigner.VerifySignature(hash, R, S);
+		}
+
+		/// <summary>Reset the internal state</summary>
+		public void Reset()
+		{
+			digest.Reset();
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/GOST3410Signer.cs b/Crypto/src/crypto/signers/GOST3410Signer.cs
new file mode 100644
index 000000000..375eeb5cc
--- /dev/null
+++ b/Crypto/src/crypto/signers/GOST3410Signer.cs
@@ -0,0 +1,132 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/**
+	 * Gost R 34.10-94 Signature Algorithm
+	 */
+	public class Gost3410Signer
+		: IDsa
+	{
+		private Gost3410KeyParameters key;
+		private SecureRandom random;
+
+		public string AlgorithmName
+		{
+			get { return "GOST3410"; }
+		}
+
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			if (forSigning)
+			{
+				if (parameters is ParametersWithRandom)
+				{
+					ParametersWithRandom rParam = (ParametersWithRandom)parameters;
+
+					this.random = rParam.Random;
+					parameters = rParam.Parameters;
+				}
+				else
+				{
+					this.random = new SecureRandom();
+				}
+
+				if (!(parameters is Gost3410PrivateKeyParameters))
+					throw new InvalidKeyException("GOST3410 private key required for signing");
+
+				this.key = (Gost3410PrivateKeyParameters) parameters;
+			}
+			else
+			{
+				if (!(parameters is Gost3410PublicKeyParameters))
+					throw new InvalidKeyException("GOST3410 public key required for signing");
+
+				this.key = (Gost3410PublicKeyParameters) parameters;
+			}
+		}
+
+		/**
+		 * generate a signature for the given message using the key we were
+		 * initialised with. For conventional Gost3410 the message should be a Gost3411
+		 * hash of the message of interest.
+		 *
+		 * @param message the message that will be verified later.
+		 */
+		public BigInteger[] GenerateSignature(
+			byte[] message)
+		{
+			byte[] mRev = new byte[message.Length]; // conversion is little-endian
+			for (int i = 0; i != mRev.Length; i++)
+			{
+				mRev[i] = message[mRev.Length - 1 - i];
+			}
+
+			BigInteger m = new BigInteger(1, mRev);
+			Gost3410Parameters parameters = key.Parameters;
+			BigInteger k;
+
+			do
+			{
+				k = new BigInteger(parameters.Q.BitLength, random);
+			}
+			while (k.CompareTo(parameters.Q) >= 0);
+
+			BigInteger r = parameters.A.ModPow(k, parameters.P).Mod(parameters.Q);
+
+			BigInteger s = k.Multiply(m).
+				Add(((Gost3410PrivateKeyParameters)key).X.Multiply(r)).
+				Mod(parameters.Q);
+
+			return new BigInteger[]{ r, s };
+		}
+
+		/**
+		 * return true if the value r and s represent a Gost3410 signature for
+		 * the passed in message for standard Gost3410 the message should be a
+		 * Gost3411 hash of the real message to be verified.
+		 */
+		public bool VerifySignature(
+			byte[]		message,
+			BigInteger	r,
+			BigInteger	s)
+		{
+			byte[] mRev = new byte[message.Length]; // conversion is little-endian
+			for (int i = 0; i != mRev.Length; i++)
+			{
+				mRev[i] = message[mRev.Length - 1 - i];
+			}
+
+			BigInteger m = new BigInteger(1, mRev);
+			Gost3410Parameters parameters = key.Parameters;
+
+			if (r.SignValue < 0 || parameters.Q.CompareTo(r) <= 0)
+			{
+				return false;
+			}
+
+			if (s.SignValue < 0 || parameters.Q.CompareTo(s) <= 0)
+			{
+				return false;
+			}
+
+			BigInteger v = m.ModPow(parameters.Q.Subtract(BigInteger.Two), parameters.Q);
+
+			BigInteger z1 = s.Multiply(v).Mod(parameters.Q);
+			BigInteger z2 = (parameters.Q.Subtract(r)).Multiply(v).Mod(parameters.Q);
+
+			z1 = parameters.A.ModPow(z1, parameters.P);
+			z2 = ((Gost3410PublicKeyParameters)key).Y.ModPow(z2, parameters.P);
+
+			BigInteger u = z1.Multiply(z2).Mod(parameters.P).Mod(parameters.Q);
+
+			return u.Equals(r);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/GenericSigner.cs b/Crypto/src/crypto/signers/GenericSigner.cs
new file mode 100644
index 000000000..1a53eee2b
--- /dev/null
+++ b/Crypto/src/crypto/signers/GenericSigner.cs
@@ -0,0 +1,129 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	public class GenericSigner
+		: ISigner
+	{
+		private readonly IAsymmetricBlockCipher engine;
+		private readonly IDigest digest;
+		private bool forSigning;
+
+		public GenericSigner(
+			IAsymmetricBlockCipher	engine,
+			IDigest					digest)
+		{
+			this.engine = engine;
+			this.digest = digest;
+		}
+
+		public string AlgorithmName
+		{
+			get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; }
+		}
+
+		/**
+		* initialise the signer for signing or verification.
+		*
+		* @param forSigning
+		*            true if for signing, false otherwise
+		* @param parameters
+		*            necessary parameters.
+		*/
+		public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			this.forSigning = forSigning;
+			AsymmetricKeyParameter k;
+
+			if (parameters is ParametersWithRandom)
+			{
+				k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+			}
+			else
+			{
+				k = (AsymmetricKeyParameter)parameters;
+			}
+
+            if (forSigning && !k.IsPrivate)
+                throw new InvalidKeyException("Signing requires private key.");
+
+			if (!forSigning && k.IsPrivate)
+                throw new InvalidKeyException("Verification requires public key.");
+
+			Reset();
+
+			engine.Init(forSigning, parameters);
+		}
+
+		/**
+		* update the internal digest with the byte b
+		*/
+		public void Update(
+			byte input)
+		{
+			digest.Update(input);
+		}
+
+		/**
+		* update the internal digest with the byte array in
+		*/
+		public void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		length)
+		{
+			digest.BlockUpdate(input, inOff, length);
+		}
+
+		/**
+		* Generate a signature for the message we've been loaded with using the key
+		* we were initialised with.
+		*/
+		public byte[] GenerateSignature()
+		{
+			if (!forSigning)
+				throw new InvalidOperationException("GenericSigner not initialised for signature generation.");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			return engine.ProcessBlock(hash, 0, hash.Length);
+		}
+
+		/**
+		* return true if the internal state represents the signature described in
+		* the passed in array.
+		*/
+		public bool VerifySignature(
+			byte[] signature)
+		{
+			if (forSigning)
+				throw new InvalidOperationException("GenericSigner not initialised for verification");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			try
+			{
+				byte[] sig = engine.ProcessBlock(signature, 0, signature.Length);
+
+				return Arrays.ConstantTimeAreEqual(sig, hash);
+			}
+			catch (Exception)
+			{
+				return false;
+			}
+		}
+
+		public void Reset()
+		{
+			digest.Reset();
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs
new file mode 100644
index 000000000..48cd719e9
--- /dev/null
+++ b/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -0,0 +1,576 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
+	/// <p>
+	/// Note: the usual length for the salt is the length of the hash
+	/// function used in bytes.</p>
+	/// </summary>
+	public class Iso9796d2PssSigner
+		: ISignerWithRecovery
+	{
+		/// <summary>
+		/// Return a reference to the recoveredMessage message.
+		/// </summary>
+		/// <returns>The full/partial recoveredMessage message.</returns>
+		/// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
+		public byte[] GetRecoveredMessage()
+		{
+			return recoveredMessage;
+		}
+
+		public const int TrailerImplicit = 0xBC;
+		public const int TrailerRipeMD160 = 0x31CC;
+		public const int TrailerRipeMD128 = 0x32CC;
+		public const int TrailerSha1 = 0x33CC;
+
+		private IDigest digest;
+		private IAsymmetricBlockCipher cipher;
+
+		private SecureRandom random;
+		private byte[] standardSalt;
+
+		private int hLen;
+		private int trailer;
+		private int keyBits;
+		private byte[] block;
+		private byte[] mBuf;
+		private int messageLength;
+		private readonly int saltLength;
+		private bool fullMessage;
+		private byte[] recoveredMessage;
+
+		/// <summary>
+		/// Generate a signer for the with either implicit or explicit trailers
+		/// for ISO9796-2, scheme 2 or 3.
+		/// </summary>
+		/// <param name="cipher">base cipher to use for signature creation/verification</param>
+		/// <param name="digest">digest to use.</param>
+		/// <param name="saltLength">length of salt in bytes.</param>
+		/// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
+		public Iso9796d2PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest,
+			int						saltLength,
+			bool					isImplicit)
+		{
+			this.cipher = cipher;
+			this.digest = digest;
+			this.hLen = digest.GetDigestSize();
+			this.saltLength = saltLength;
+
+			if (isImplicit)
+			{
+				trailer = TrailerImplicit;
+			}
+			else
+			{
+				if (digest is Sha1Digest)
+				{
+					trailer = TrailerSha1;
+				}
+				else if (digest is RipeMD160Digest)
+				{
+					trailer = TrailerRipeMD160;
+				}
+				else if (digest is RipeMD128Digest)
+				{
+					trailer = TrailerRipeMD128;
+				}
+				else
+				{
+					throw new ArgumentException("no valid trailer for digest");
+				}
+			}
+		}
+
+		/// <summary> Constructor for a signer with an explicit digest trailer.
+		///
+		/// </summary>
+		/// <param name="cipher">cipher to use.
+		/// </param>
+		/// <param name="digest">digest to sign with.
+		/// </param>
+		/// <param name="saltLength">length of salt in bytes.
+		/// </param>
+		public Iso9796d2PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest,
+			int						saltLength)
+			: this(cipher, digest, saltLength, false)
+		{
+		}
+
+		public string AlgorithmName
+		{
+			get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; }
+		}
+
+		/// <summary>Initialise the signer.</summary>
+		/// <param name="forSigning">true if for signing, false if for verification.</param>
+		/// <param name="parameters">parameters for signature generation/verification. If the
+		/// parameters are for generation they should be a ParametersWithRandom,
+		/// a ParametersWithSalt, or just an RsaKeyParameters object. If RsaKeyParameters
+		/// are passed in a SecureRandom will be created.
+		/// </param>
+		/// <exception cref="ArgumentException">if wrong parameter type or a fixed
+		/// salt is passed in which is the wrong length.
+		/// </exception>
+		public virtual void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			RsaKeyParameters kParam;
+			if (parameters is ParametersWithRandom)
+			{
+				ParametersWithRandom p = (ParametersWithRandom) parameters;
+
+				kParam = (RsaKeyParameters) p.Parameters;
+
+				if (forSigning)
+				{
+					random = p.Random;
+				}
+			}
+			else if (parameters is ParametersWithSalt)
+			{
+				if (!forSigning)
+					throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters");
+
+				ParametersWithSalt p = (ParametersWithSalt) parameters;
+
+				kParam = (RsaKeyParameters) p.Parameters;
+				standardSalt = p.GetSalt();
+
+				if (standardSalt.Length != saltLength)
+					throw new ArgumentException("Fixed salt is of wrong length");
+			}
+			else
+			{
+				kParam = (RsaKeyParameters) parameters;
+
+				if (forSigning)
+				{
+					random = new SecureRandom();
+				}
+			}
+
+			cipher.Init(forSigning, kParam);
+
+			keyBits = kParam.Modulus.BitLength;
+
+			block = new byte[(keyBits + 7) / 8];
+
+			if (trailer == TrailerImplicit)
+			{
+				mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1];
+			}
+			else
+			{
+				mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2];
+			}
+
+			Reset();
+		}
+
+		/// <summary> compare two byte arrays - constant time.</summary>
+		private bool IsSameAs(byte[] a, byte[] b)
+		{
+			if (messageLength != b.Length)
+			{
+				return false;
+			}
+
+			bool isOkay = true;
+
+			for (int i = 0; i != b.Length; i++)
+			{
+				if (a[i] != b[i])
+				{
+					isOkay = false;
+				}
+			}
+
+			return isOkay;
+		}
+
+		/// <summary> clear possible sensitive data</summary>
+		private void  ClearBlock(
+			byte[] block)
+		{
+			Array.Clear(block, 0, block.Length);
+		}
+
+		public virtual void UpdateWithRecoveredMessage(
+			byte[] signature)
+		{
+			// TODO
+			throw Platform.CreateNotImplementedException("UpdateWithRecoveredMessage");
+		}
+
+		/// <summary> update the internal digest with the byte b</summary>
+		public virtual void Update(
+			byte input)
+		{
+			if (messageLength < mBuf.Length)
+			{
+				mBuf[messageLength++] = input;
+			}
+			else
+			{
+				digest.Update(input);
+			}
+		}
+
+		/// <summary> update the internal digest with the byte array in</summary>
+		public virtual void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		length)
+		{
+			while (length > 0 && messageLength < mBuf.Length)
+			{
+				this.Update(input[inOff]);
+				inOff++;
+				length--;
+			}
+
+			if (length > 0)
+			{
+				digest.BlockUpdate(input, inOff, length);
+			}
+		}
+
+		/// <summary> reset the internal state</summary>
+		public virtual void Reset()
+		{
+			digest.Reset();
+			messageLength = 0;
+			if (mBuf != null)
+			{
+				ClearBlock(mBuf);
+			}
+			if (recoveredMessage != null)
+			{
+				ClearBlock(recoveredMessage);
+				recoveredMessage = null;
+			}
+			fullMessage = false;
+		}
+
+		/// <summary> Generate a signature for the loaded message using the key we were
+		/// initialised with.
+		/// </summary>
+		public byte[] GenerateSignature()
+		{
+			int digSize = digest.GetDigestSize();
+			byte[] m2Hash = new byte[digSize];
+			digest.DoFinal(m2Hash, 0);
+
+			byte[] C = new byte[8];
+			LtoOSP(messageLength * 8, C);
+
+			digest.BlockUpdate(C, 0, C.Length);
+			digest.BlockUpdate(mBuf, 0, messageLength);
+			digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
+
+			byte[] salt;
+			if (standardSalt != null)
+			{
+				salt = standardSalt;
+			}
+			else
+			{
+				salt = new byte[saltLength];
+				random.NextBytes(salt);
+			}
+
+			digest.BlockUpdate(salt, 0, salt.Length);
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			int tLength = 2;
+			if (trailer == TrailerImplicit)
+			{
+				tLength = 1;
+			}
+
+			int off = block.Length - messageLength - salt.Length - hLen - tLength - 1;
+
+			block[off] = (byte) (0x01);
+
+			Array.Copy(mBuf, 0, block, off + 1, messageLength);
+			Array.Copy(salt, 0, block, off + 1 + messageLength, salt.Length);
+
+			byte[] dbMask = MaskGeneratorFunction1(hash, 0, hash.Length, block.Length - hLen - tLength);
+			for (int i = 0; i != dbMask.Length; i++)
+			{
+				block[i] ^= dbMask[i];
+			}
+
+			Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen);
+
+			if (trailer == TrailerImplicit)
+			{
+				block[block.Length - 1] = (byte)TrailerImplicit;
+			}
+			else
+			{
+				block[block.Length - 2] = (byte) ((uint)trailer >> 8);
+				block[block.Length - 1] = (byte) trailer;
+			}
+
+			block[0] &= (byte) (0x7f);
+
+			byte[] b = cipher.ProcessBlock(block, 0, block.Length);
+
+			ClearBlock(mBuf);
+			ClearBlock(block);
+			messageLength = 0;
+
+			return b;
+		}
+
+		/// <summary> return true if the signature represents a ISO9796-2 signature
+		/// for the passed in message.
+		/// </summary>
+		public virtual bool VerifySignature(
+			byte[] signature)
+		{
+			byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
+
+			//
+			// adjust block size for leading zeroes if necessary
+			//
+			int expectedSize = (keyBits + 7) / 8;
+			if (block.Length < expectedSize)
+			{
+				byte[] tmp = new byte[expectedSize];
+				block.CopyTo(tmp, tmp.Length - block.Length);
+				ClearBlock(block);
+				block = tmp;
+			}
+
+			int tLength;
+
+			if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
+			{
+				tLength = 1;
+			}
+			else
+			{
+				int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
+
+				switch (sigTrail)
+				{
+					case TrailerRipeMD160:
+						if (!(digest is RipeMD160Digest))
+						{
+							throw new ArgumentException("signer should be initialised with RipeMD160");
+						}
+						break;
+					case TrailerSha1:
+						if (!(digest is Sha1Digest))
+						{
+							throw new ArgumentException("signer should be initialised with SHA1");
+						}
+						break;
+					case TrailerRipeMD128:
+						if (!(digest is RipeMD128Digest))
+						{
+							throw new ArgumentException("signer should be initialised with RipeMD128");
+						}
+						break;
+					default:
+						throw new ArgumentException("unrecognised hash in signature");
+				}
+
+				tLength = 2;
+			}
+
+			//
+			// calculate H(m2)
+			//
+			byte[] m2Hash = new byte[hLen];
+			digest.DoFinal(m2Hash, 0);
+
+			//
+			// remove the mask
+			//
+			byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - tLength, hLen, block.Length - hLen - tLength);
+			for (int i = 0; i != dbMask.Length; i++)
+			{
+				block[i] ^= dbMask[i];
+			}
+
+			block[0] &= 0x7f;
+
+			//
+			// find out how much padding we've got
+			//
+			int mStart = 0;
+			while (mStart < block.Length)
+			{
+				if (block[mStart++] == 0x01)
+					break;
+			}
+
+			if (mStart >= block.Length)
+			{
+				ClearBlock(block);
+				return false;
+			}
+
+			fullMessage = (mStart > 1);
+
+			// TODO Should we check if a standardSalt was set and, if so, use its length instead?
+			recoveredMessage = new byte[dbMask.Length - mStart - saltLength];
+
+			Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+
+			//
+			// check the hashes
+			//
+			byte[] C = new byte[8];
+			LtoOSP(recoveredMessage.Length * 8, C);
+
+			digest.BlockUpdate(C, 0, C.Length);
+
+			if (recoveredMessage.Length != 0)
+			{
+				digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
+			}
+
+			digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
+
+			// Update for the salt
+			digest.BlockUpdate(block, mStart + recoveredMessage.Length, saltLength);
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			int off = block.Length - tLength - hash.Length;
+
+			// TODO ConstantTimeAreEqual with offset for one array
+
+			bool isOkay = true;
+			for (int i = 0; i != hash.Length; i++)
+			{
+				if (hash[i] != block[off + i])
+				{
+					isOkay = false;
+				}
+			}
+
+			ClearBlock(block);
+			ClearBlock(hash);
+
+			if (!isOkay)
+			{
+				fullMessage = false;
+				ClearBlock(recoveredMessage);
+				return false;
+			}
+
+			//
+			// if they've input a message check what we've recovered against
+			// what was input.
+			//
+			if (messageLength != 0)
+			{
+				if (!IsSameAs(mBuf, recoveredMessage))
+				{
+					ClearBlock(mBuf);
+					return false;
+				}
+
+				messageLength = 0;
+			}
+
+			ClearBlock(mBuf);
+			return true;
+		}
+
+		/// <summary>
+		/// Return true if the full message was recoveredMessage.
+		/// </summary>
+		/// <returns>true on full message recovery, false otherwise, or if not sure.</returns>
+		/// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
+		public virtual bool HasFullMessage()
+		{
+			return fullMessage;
+		}
+
+		/// <summary> int to octet string.</summary>
+		/// <summary> int to octet string.</summary>
+		private void ItoOSP(
+			int		i,
+			byte[]	sp)
+		{
+			sp[0] = (byte)((uint)i >> 24);
+			sp[1] = (byte)((uint)i >> 16);
+			sp[2] = (byte)((uint)i >> 8);
+			sp[3] = (byte)((uint)i >> 0);
+		}
+
+		/// <summary> long to octet string.</summary>
+		private void  LtoOSP(long l, byte[] sp)
+		{
+			sp[0] = (byte)((ulong)l >> 56);
+			sp[1] = (byte)((ulong)l >> 48);
+			sp[2] = (byte)((ulong)l >> 40);
+			sp[3] = (byte)((ulong)l >> 32);
+			sp[4] = (byte)((ulong)l >> 24);
+			sp[5] = (byte)((ulong)l >> 16);
+			sp[6] = (byte)((ulong)l >> 8);
+			sp[7] = (byte)((ulong)l >> 0);
+		}
+
+		/// <summary> mask generator function, as described in Pkcs1v2.</summary>
+		private byte[] MaskGeneratorFunction1(
+			byte[]	Z,
+			int		zOff,
+			int		zLen,
+			int		length)
+		{
+			byte[] mask = new byte[length];
+			byte[] hashBuf = new byte[hLen];
+			byte[] C = new byte[4];
+			int counter = 0;
+
+			digest.Reset();
+
+			do
+			{
+				ItoOSP(counter, C);
+
+				digest.BlockUpdate(Z, zOff, zLen);
+				digest.BlockUpdate(C, 0, C.Length);
+				digest.DoFinal(hashBuf, 0);
+
+				Array.Copy(hashBuf, 0, mask, counter * hLen, hLen);
+			}
+			while (++counter < (length / hLen));
+
+			if ((counter * hLen) < length)
+			{
+				ItoOSP(counter, C);
+
+				digest.BlockUpdate(Z, zOff, zLen);
+				digest.BlockUpdate(C, 0, C.Length);
+				digest.DoFinal(hashBuf, 0);
+
+				Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen));
+			}
+
+			return mask;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/Iso9796d2Signer.cs b/Crypto/src/crypto/signers/Iso9796d2Signer.cs
new file mode 100644
index 000000000..8ff87e8ee
--- /dev/null
+++ b/Crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -0,0 +1,557 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 1)</summary>
+	public class Iso9796d2Signer : ISignerWithRecovery
+	{
+		/// <summary>
+		/// Return a reference to the recoveredMessage message.
+		/// </summary>
+		/// <returns>The full/partial recoveredMessage message.</returns>
+		/// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
+		public byte[] GetRecoveredMessage()
+		{
+			return recoveredMessage;
+		}
+
+		public const int TrailerImplicit = 0xBC;
+		public const int TrailerRipeMD160 = 0x31CC;
+		public const int TrailerRipeMD128 = 0x32CC;
+		public const int TrailerSha1 = 0x33CC;
+		public const int TrailerSha256 = 0x34CC;
+		public const int TrailerSha512 = 0x35CC;
+		public const int TrailerSha384 = 0x36CC;
+		public const int TrailerWhirlpool = 0x37CC;
+
+		private static IDictionary trailerMap = Platform.CreateHashtable();
+
+		static Iso9796d2Signer()
+		{
+			trailerMap.Add("RIPEMD128", TrailerRipeMD128);
+			trailerMap.Add("RIPEMD160", TrailerRipeMD160);
+
+			trailerMap.Add("SHA-1", TrailerSha1);
+			trailerMap.Add("SHA-256", TrailerSha256);
+			trailerMap.Add("SHA-384", TrailerSha384);
+			trailerMap.Add("SHA-512", TrailerSha512);
+
+			trailerMap.Add("Whirlpool", TrailerWhirlpool);
+		}
+
+		private IDigest digest;
+		private IAsymmetricBlockCipher cipher;
+
+		private int trailer;
+		private int keyBits;
+		private byte[] block;
+		private byte[] mBuf;
+		private int messageLength;
+		private bool fullMessage;
+		private byte[] recoveredMessage;
+
+		private byte[] preSig;
+		private byte[] preBlock;
+
+		/// <summary>
+		/// Generate a signer for the with either implicit or explicit trailers
+		/// for ISO9796-2.
+		/// </summary>
+		/// <param name="cipher">base cipher to use for signature creation/verification</param>
+		/// <param name="digest">digest to use.</param>
+		/// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
+		public Iso9796d2Signer(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest,
+			bool					isImplicit)
+		{
+			this.cipher = cipher;
+			this.digest = digest;
+
+			if (isImplicit)
+			{
+				trailer = TrailerImplicit;
+			}
+			else
+			{
+				string digestName = digest.AlgorithmName;
+
+				if (trailerMap.Contains(digestName))
+				{
+					trailer = (int)trailerMap[digest.AlgorithmName];
+				}
+				else
+				{
+					throw new System.ArgumentException("no valid trailer for digest");
+				}
+			}
+		}
+
+		/// <summary> Constructor for a signer with an explicit digest trailer.
+		///
+		/// </summary>
+		/// <param name="cipher">cipher to use.
+		/// </param>
+		/// <param name="digest">digest to sign with.
+		/// </param>
+		public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest)
+			: this(cipher, digest, false)
+		{
+		}
+
+		public string AlgorithmName
+		{
+			get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; }
+		}
+
+		public virtual void Init(bool forSigning, ICipherParameters parameters)
+		{
+			RsaKeyParameters kParam = (RsaKeyParameters) parameters;
+
+			cipher.Init(forSigning, kParam);
+
+			keyBits = kParam.Modulus.BitLength;
+
+			block = new byte[(keyBits + 7) / 8];
+			if (trailer == TrailerImplicit)
+			{
+				mBuf = new byte[block.Length - digest.GetDigestSize() - 2];
+			}
+			else
+			{
+				mBuf = new byte[block.Length - digest.GetDigestSize() - 3];
+			}
+
+			Reset();
+		}
+
+		/// <summary> compare two byte arrays - constant time.</summary>
+		private bool IsSameAs(byte[] a, byte[] b)
+		{
+			int checkLen;
+			if (messageLength > mBuf.Length)
+			{
+				if (mBuf.Length > b.Length)
+				{
+					return false;
+				}
+
+				checkLen = mBuf.Length;
+			}
+			else
+			{
+				if (messageLength != b.Length)
+				{
+					return false;
+				}
+
+				checkLen = b.Length;
+			}
+
+			bool isOkay = true;
+
+			for (int i = 0; i != checkLen; i++)
+			{
+				if (a[i] != b[i])
+				{
+					isOkay = false;
+				}
+			}
+
+			return isOkay;
+		}
+
+		/// <summary> clear possible sensitive data</summary>
+		private void  ClearBlock(
+			byte[] block)
+		{
+			Array.Clear(block, 0, block.Length);
+		}
+
+		public virtual void UpdateWithRecoveredMessage(
+			byte[] signature)
+		{
+			byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
+
+			if (((block[0] & 0xC0) ^ 0x40) != 0)
+				throw new InvalidCipherTextException("malformed signature");
+
+			if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
+				throw new InvalidCipherTextException("malformed signature");
+
+			int delta = 0;
+
+			if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
+			{
+				delta = 1;
+			}
+			else
+			{
+				int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
+
+				string digestName = digest.AlgorithmName;
+				if (!trailerMap.Contains(digestName))
+					throw new ArgumentException("unrecognised hash in signature");
+				if (sigTrail != (int)trailerMap[digestName])
+					throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
+
+				delta = 2;
+			}
+
+			//
+			// find out how much padding we've got
+			//
+			int mStart = 0;
+
+			for (mStart = 0; mStart != block.Length; mStart++)
+			{
+				if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
+					break;
+			}
+
+			mStart++;
+
+			int off = block.Length - delta - digest.GetDigestSize();
+
+			//
+			// there must be at least one byte of message string
+			//
+			if ((off - mStart) <= 0)
+				throw new InvalidCipherTextException("malformed block");
+
+			//
+			// if we contain the whole message as well, check the hash of that.
+			//
+			if ((block[0] & 0x20) == 0)
+			{
+				fullMessage = true;
+
+				recoveredMessage = new byte[off - mStart];
+				Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+			}
+			else
+			{
+				fullMessage = false;
+
+				recoveredMessage = new byte[off - mStart];
+				Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+			}
+
+			preSig = signature;
+			preBlock = block;
+
+			digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
+			messageLength = recoveredMessage.Length;
+		}
+
+		/// <summary> update the internal digest with the byte b</summary>
+		public void Update(
+			byte input)
+		{
+			digest.Update(input);
+
+			if (preSig == null && messageLength < mBuf.Length)
+			{
+				mBuf[messageLength] = input;
+			}
+
+			messageLength++;
+		}
+
+		/// <summary> update the internal digest with the byte array in</summary>
+		public void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		length)
+		{
+			digest.BlockUpdate(input, inOff, length);
+
+			if (preSig == null && messageLength < mBuf.Length)
+			{
+				for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++)
+				{
+					mBuf[messageLength + i] = input[inOff + i];
+				}
+			}
+
+			messageLength += length;
+		}
+
+		/// <summary> reset the internal state</summary>
+		public virtual void Reset()
+		{
+			digest.Reset();
+			messageLength = 0;
+			ClearBlock(mBuf);
+
+			if (recoveredMessage != null)
+			{
+				ClearBlock(recoveredMessage);
+			}
+
+			recoveredMessage = null;
+			fullMessage = false;
+		}
+
+		/// <summary> Generate a signature for the loaded message using the key we were
+		/// initialised with.
+		/// </summary>
+		public virtual byte[] GenerateSignature()
+		{
+			int digSize = digest.GetDigestSize();
+
+			int t = 0;
+			int delta = 0;
+
+			if (trailer == TrailerImplicit)
+			{
+				t = 8;
+				delta = block.Length - digSize - 1;
+				digest.DoFinal(block, delta);
+				block[block.Length - 1] = (byte) TrailerImplicit;
+			}
+			else
+			{
+				t = 16;
+				delta = block.Length - digSize - 2;
+				digest.DoFinal(block, delta);
+				block[block.Length - 2] = (byte) ((uint)trailer >> 8);
+				block[block.Length - 1] = (byte) trailer;
+			}
+
+			byte header = 0;
+			int x = (digSize + messageLength) * 8 + t + 4 - keyBits;
+
+			if (x > 0)
+			{
+				int mR = messageLength - ((x + 7) / 8);
+				header = (byte) (0x60);
+
+				delta -= mR;
+
+				Array.Copy(mBuf, 0, block, delta, mR);
+			}
+			else
+			{
+				header = (byte) (0x40);
+				delta -= messageLength;
+
+				Array.Copy(mBuf, 0, block, delta, messageLength);
+			}
+
+			if ((delta - 1) > 0)
+			{
+				for (int i = delta - 1; i != 0; i--)
+				{
+					block[i] = (byte) 0xbb;
+				}
+				block[delta - 1] ^= (byte) 0x01;
+				block[0] = (byte) 0x0b;
+				block[0] |= header;
+			}
+			else
+			{
+				block[0] = (byte) 0x0a;
+				block[0] |= header;
+			}
+
+			byte[] b = cipher.ProcessBlock(block, 0, block.Length);
+
+			ClearBlock(mBuf);
+			ClearBlock(block);
+
+			return b;
+		}
+
+		/// <summary> return true if the signature represents a ISO9796-2 signature
+		/// for the passed in message.
+		/// </summary>
+		public virtual bool VerifySignature(byte[] signature)
+		{
+			byte[] block;
+			bool updateWithRecoveredCalled;
+
+			if (preSig == null)
+			{
+				updateWithRecoveredCalled = false;
+				try
+				{
+					block = cipher.ProcessBlock(signature, 0, signature.Length);
+				}
+				catch (Exception)
+				{
+					return false;
+				}
+			}
+			else
+			{
+				if (!Arrays.AreEqual(preSig, signature))
+					throw new InvalidOperationException("updateWithRecoveredMessage called on different signature");
+
+				updateWithRecoveredCalled = true;
+				block = preBlock;
+
+				preSig = null;
+				preBlock = null;
+			}
+
+			if (((block[0] & 0xC0) ^ 0x40) != 0)
+				return ReturnFalse(block);
+
+			if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
+				return ReturnFalse(block);
+
+			int delta = 0;
+
+			if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
+			{
+				delta = 1;
+			}
+			else
+			{
+				int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
+
+				string digestName = digest.AlgorithmName;
+				if (!trailerMap.Contains(digestName))
+					throw new ArgumentException("unrecognised hash in signature");
+				if (sigTrail != (int)trailerMap[digestName])
+					throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
+
+				delta = 2;
+			}
+
+			//
+			// find out how much padding we've got
+			//
+			int mStart = 0;
+			for (; mStart != block.Length; mStart++)
+			{
+				if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
+				{
+					break;
+				}
+			}
+
+			mStart++;
+
+			//
+			// check the hashes
+			//
+			byte[] hash = new byte[digest.GetDigestSize()];
+
+			int off = block.Length - delta - hash.Length;
+
+			//
+			// there must be at least one byte of message string
+			//
+			if ((off - mStart) <= 0)
+			{
+				return ReturnFalse(block);
+			}
+
+			//
+			// if we contain the whole message as well, check the hash of that.
+			//
+			if ((block[0] & 0x20) == 0)
+			{
+				fullMessage = true;
+
+				// check right number of bytes passed in.
+				if (messageLength > off - mStart)
+				{
+					return ReturnFalse(block);
+				}
+
+				digest.Reset();
+				digest.BlockUpdate(block, mStart, off - mStart);
+				digest.DoFinal(hash, 0);
+
+				bool isOkay = true;
+				
+				for (int i = 0; i != hash.Length; i++)
+				{
+					block[off + i] ^= hash[i];
+					if (block[off + i] != 0)
+					{
+						isOkay = false;
+					}
+				}
+
+				if (!isOkay)
+				{
+					return ReturnFalse(block);
+				}
+
+				recoveredMessage = new byte[off - mStart];
+				Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+			}
+			else
+			{
+				fullMessage = false;
+
+				digest.DoFinal(hash, 0);
+
+				bool isOkay = true;
+
+				for (int i = 0; i != hash.Length; i++)
+				{
+					block[off + i] ^= hash[i];
+					if (block[off + i] != 0)
+					{
+						isOkay = false;
+					}
+				}
+
+				if (!isOkay)
+				{
+					return ReturnFalse(block);
+				}
+
+				recoveredMessage = new byte[off - mStart];
+				Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
+			}
+
+			//
+			// if they've input a message check what we've recovered against
+			// what was input.
+			//
+			if (messageLength != 0 && !updateWithRecoveredCalled)
+			{
+				if (!IsSameAs(mBuf, recoveredMessage))
+				{
+//					ClearBlock(recoveredMessage);
+					return ReturnFalse(block);
+				}
+			}
+
+			ClearBlock(mBuf);
+			ClearBlock(block);
+
+			return true;
+		}
+
+		private bool ReturnFalse(byte[] block)
+		{
+			ClearBlock(mBuf);
+			ClearBlock(block);
+
+			return false;
+		}
+
+		/// <summary>
+		/// Return true if the full message was recoveredMessage.
+		/// </summary>
+		/// <returns> true on full message recovery, false otherwise.</returns>
+		/// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
+		public virtual bool HasFullMessage()
+		{
+			return fullMessage;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/PssSigner.cs b/Crypto/src/crypto/signers/PssSigner.cs
new file mode 100644
index 000000000..6900224f3
--- /dev/null
+++ b/Crypto/src/crypto/signers/PssSigner.cs
@@ -0,0 +1,345 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+	/// <summary> RSA-PSS as described in Pkcs# 1 v 2.1.
+	/// <p>
+	/// Note: the usual value for the salt length is the number of
+	/// bytes in the hash function.</p>
+	/// </summary>
+	public class PssSigner
+		: ISigner
+	{
+		public const byte TrailerImplicit = (byte)0xBC;
+
+		private readonly IDigest contentDigest1, contentDigest2;
+		private readonly IDigest mgfDigest;
+		private readonly IAsymmetricBlockCipher cipher;
+
+		private SecureRandom random;
+
+		private int hLen;
+		private int mgfhLen;
+		private int sLen;
+		private int emBits;
+		private byte[] salt;
+		private byte[] mDash;
+		private byte[] block;
+		private byte trailer;
+
+		public static PssSigner CreateRawSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest)
+		{
+			return new PssSigner(cipher, new NullDigest(), digest, digest, digest.GetDigestSize(), TrailerImplicit);
+		}
+
+		public static PssSigner CreateRawSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					contentDigest,
+			IDigest					mgfDigest,
+			int						saltLen,
+			byte					trailer)
+		{
+			return new PssSigner(cipher, new NullDigest(), contentDigest, mgfDigest, saltLen, trailer);
+		}
+
+		public PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest)
+			: this(cipher, digest, digest.GetDigestSize())
+		{
+		}
+
+		/// <summary>Basic constructor</summary>
+		/// <param name="cipher">the asymmetric cipher to use.</param>
+		/// <param name="digest">the digest to use.</param>
+		/// <param name="saltLen">the length of the salt to use (in bytes).</param>
+		public PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest,
+			int						saltLen)
+			: this(cipher, digest, saltLen, TrailerImplicit)
+		{
+		}
+
+		public PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					contentDigest,
+			IDigest					mgfDigest,
+			int						saltLen)
+			: this(cipher, contentDigest, mgfDigest, saltLen, TrailerImplicit)
+		{
+		}
+
+		public PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					digest,
+			int						saltLen,
+			byte					trailer)
+			: this(cipher, digest, digest, saltLen, TrailerImplicit)
+		{
+		}
+
+		public PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					contentDigest,
+			IDigest					mgfDigest,
+			int						saltLen,
+			byte					trailer)
+			: this(cipher, contentDigest, contentDigest, mgfDigest, saltLen, trailer)
+		{
+		}
+
+		private PssSigner(
+			IAsymmetricBlockCipher	cipher,
+			IDigest					contentDigest1,
+			IDigest					contentDigest2,
+			IDigest					mgfDigest,
+			int						saltLen,
+			byte					trailer)
+		{
+			this.cipher = cipher;
+			this.contentDigest1 = contentDigest1;
+			this.contentDigest2 = contentDigest2;
+			this.mgfDigest = mgfDigest;
+			this.hLen = contentDigest2.GetDigestSize();
+			this.mgfhLen = mgfDigest.GetDigestSize();
+			this.sLen = saltLen;
+			this.salt = new byte[saltLen];
+			this.mDash = new byte[8 + saltLen + hLen];
+			this.trailer = trailer;
+		}
+
+		public string AlgorithmName
+		{
+			get { return mgfDigest.AlgorithmName + "withRSAandMGF1"; }
+		}
+
+		public virtual void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+		{
+			if (parameters is ParametersWithRandom)
+			{
+				ParametersWithRandom p = (ParametersWithRandom) parameters;
+
+				parameters = p.Parameters;
+				random = p.Random;
+			}
+			else
+			{
+				if (forSigning)
+				{
+					random = new SecureRandom();
+				}
+			}
+
+			cipher.Init(forSigning, parameters);
+
+			RsaKeyParameters kParam;
+			if (parameters is RsaBlindingParameters)
+			{
+				kParam = ((RsaBlindingParameters) parameters).PublicKey;
+			}
+			else
+			{
+				kParam = (RsaKeyParameters) parameters;
+			}
+
+			emBits = kParam.Modulus.BitLength - 1;
+
+			if (emBits < (8 * hLen + 8 * sLen + 9))
+				throw new ArgumentException("key too small for specified hash and salt lengths");
+
+			block = new byte[(emBits + 7) / 8];
+		}
+
+		/// <summary> clear possible sensitive data</summary>
+		private void ClearBlock(
+			byte[] block)
+		{
+			Array.Clear(block, 0, block.Length);
+		}
+
+		/// <summary> update the internal digest with the byte b</summary>
+		public virtual void Update(
+			byte input)
+		{
+			contentDigest1.Update(input);
+		}
+
+		/// <summary> update the internal digest with the byte array in</summary>
+		public virtual void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		length)
+		{
+			contentDigest1.BlockUpdate(input, inOff, length);
+		}
+
+		/// <summary> reset the internal state</summary>
+		public virtual void Reset()
+		{
+			contentDigest1.Reset();
+		}
+
+		/// <summary> Generate a signature for the message we've been loaded with using
+		/// the key we were initialised with.
+		/// </summary>
+		public virtual byte[] GenerateSignature()
+		{
+			contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen);
+
+			if (sLen != 0)
+			{
+				random.NextBytes(salt);
+				salt.CopyTo(mDash, mDash.Length - sLen);
+			}
+
+			byte[] h = new byte[hLen];
+
+			contentDigest2.BlockUpdate(mDash, 0, mDash.Length);
+
+			contentDigest2.DoFinal(h, 0);
+
+			block[block.Length - sLen - 1 - hLen - 1] = (byte) (0x01);
+			salt.CopyTo(block, block.Length - sLen - hLen - 1);
+
+			byte[] dbMask = MaskGeneratorFunction1(h, 0, h.Length, block.Length - hLen - 1);
+			for (int i = 0; i != dbMask.Length; i++)
+			{
+				block[i] ^= dbMask[i];
+			}
+
+			block[0] &= (byte) ((0xff >> ((block.Length * 8) - emBits)));
+
+			h.CopyTo(block, block.Length - hLen - 1);
+
+			block[block.Length - 1] = trailer;
+
+			byte[] b = cipher.ProcessBlock(block, 0, block.Length);
+
+			ClearBlock(block);
+
+			return b;
+		}
+
+		/// <summary> return true if the internal state represents the signature described
+		/// in the passed in array.
+		/// </summary>
+		public virtual bool VerifySignature(
+			byte[] signature)
+		{
+			contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen);
+
+			byte[] b = cipher.ProcessBlock(signature, 0, signature.Length);
+			b.CopyTo(block, block.Length - b.Length);
+
+			if (block[block.Length - 1] != trailer)
+			{
+				ClearBlock(block);
+				return false;
+			}
+
+			byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - 1, hLen, block.Length - hLen - 1);
+
+			for (int i = 0; i != dbMask.Length; i++)
+			{
+				block[i] ^= dbMask[i];
+			}
+
+			block[0] &= (byte) ((0xff >> ((block.Length * 8) - emBits)));
+
+			for (int i = 0; i != block.Length - hLen - sLen - 2; i++)
+			{
+				if (block[i] != 0)
+				{
+					ClearBlock(block);
+					return false;
+				}
+			}
+
+			if (block[block.Length - hLen - sLen - 2] != 0x01)
+			{
+				ClearBlock(block);
+				return false;
+			}
+
+			Array.Copy(block, block.Length - sLen - hLen - 1, mDash, mDash.Length - sLen, sLen);
+
+			contentDigest2.BlockUpdate(mDash, 0, mDash.Length);
+			contentDigest2.DoFinal(mDash, mDash.Length - hLen);
+
+			for (int i = block.Length - hLen - 1, j = mDash.Length - hLen; j != mDash.Length; i++, j++)
+			{
+				if ((block[i] ^ mDash[j]) != 0)
+				{
+					ClearBlock(mDash);
+					ClearBlock(block);
+					return false;
+				}
+			}
+
+			ClearBlock(mDash);
+			ClearBlock(block);
+
+			return true;
+		}
+
+		/// <summary> int to octet string.</summary>
+		private void ItoOSP(
+			int		i,
+			byte[]	sp)
+		{
+			sp[0] = (byte)((uint) i >> 24);
+			sp[1] = (byte)((uint) i >> 16);
+			sp[2] = (byte)((uint) i >> 8);
+			sp[3] = (byte)((uint) i >> 0);
+		}
+
+		/// <summary> mask generator function, as described in Pkcs1v2.</summary>
+		private byte[] MaskGeneratorFunction1(
+			byte[]	Z,
+			int		zOff,
+			int		zLen,
+			int		length)
+		{
+			byte[] mask = new byte[length];
+			byte[] hashBuf = new byte[mgfhLen];
+			byte[] C = new byte[4];
+			int counter = 0;
+
+			mgfDigest.Reset();
+
+			while (counter < (length / mgfhLen))
+			{
+				ItoOSP(counter, C);
+
+				mgfDigest.BlockUpdate(Z, zOff, zLen);
+				mgfDigest.BlockUpdate(C, 0, C.Length);
+				mgfDigest.DoFinal(hashBuf, 0);
+
+				hashBuf.CopyTo(mask, counter * mgfhLen);
+				++counter;
+			}
+
+			if ((counter * mgfhLen) < length)
+			{
+				ItoOSP(counter, C);
+
+				mgfDigest.BlockUpdate(Z, zOff, zLen);
+				mgfDigest.BlockUpdate(C, 0, C.Length);
+				mgfDigest.DoFinal(hashBuf, 0);
+
+				Array.Copy(hashBuf, 0, mask, counter * mgfhLen, mask.Length - (counter * mgfhLen));
+			}
+
+			return mask;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/signers/RsaDigestSigner.cs b/Crypto/src/crypto/signers/RsaDigestSigner.cs
new file mode 100644
index 000000000..f57bfc83d
--- /dev/null
+++ b/Crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -0,0 +1,228 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    public class RsaDigestSigner
+		: ISigner
+    {
+        private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine());
+        private readonly AlgorithmIdentifier algId;
+		private readonly IDigest digest;
+		private bool forSigning;
+
+		private static readonly IDictionary oidMap = Platform.CreateHashtable();
+
+		/// <summary>
+        /// Load oid table.
+        /// </summary>
+        static RsaDigestSigner()
+        {
+            oidMap["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128;
+            oidMap["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160;
+            oidMap["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256;
+
+            oidMap["SHA-1"] = X509ObjectIdentifiers.IdSha1;
+            oidMap["SHA-224"] = NistObjectIdentifiers.IdSha224;
+            oidMap["SHA-256"] = NistObjectIdentifiers.IdSha256;
+            oidMap["SHA-384"] = NistObjectIdentifiers.IdSha384;
+            oidMap["SHA-512"] = NistObjectIdentifiers.IdSha512;
+
+            oidMap["MD2"] = PkcsObjectIdentifiers.MD2;
+            oidMap["MD4"] = PkcsObjectIdentifiers.MD4;
+            oidMap["MD5"] = PkcsObjectIdentifiers.MD5;
+        }
+
+		public RsaDigestSigner(
+			IDigest digest)
+        {
+            this.digest = digest;
+
+			string algName = digest.AlgorithmName;
+			if (algName.Equals("NULL"))
+			{
+				this.algId = null;
+			}
+			else
+			{
+				this.algId = new AlgorithmIdentifier(
+					(DerObjectIdentifier)oidMap[digest.AlgorithmName], DerNull.Instance);
+			}
+        }
+
+		public string AlgorithmName
+        {
+            get { return digest.AlgorithmName + "withRSA"; }
+        }
+
+		/**
+         * Initialise the signer for signing or verification.
+         *
+         * @param forSigning true if for signing, false otherwise
+         * @param param necessary parameters.
+         */
+        public void Init(
+			bool				forSigning,
+			ICipherParameters	parameters)
+        {
+            this.forSigning = forSigning;
+            AsymmetricKeyParameter k;
+
+            if (parameters is ParametersWithRandom)
+            {
+                k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+            }
+            else
+            {
+                k = (AsymmetricKeyParameter)parameters;
+            }
+
+            if (forSigning && !k.IsPrivate)
+                throw new InvalidKeyException("Signing requires private key.");
+
+			if (!forSigning && k.IsPrivate)
+                throw new InvalidKeyException("Verification requires public key.");
+
+			Reset();
+
+            rsaEngine.Init(forSigning, parameters);
+        }
+
+        /**
+         * update the internal digest with the byte b
+         */
+        public void Update(
+			byte input)
+        {
+            digest.Update(input);
+        }
+
+        /**
+         * update the internal digest with the byte array in
+         */
+        public void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		length)
+        {
+            digest.BlockUpdate(input, inOff, length);
+        }
+
+        /**
+         * Generate a signature for the message we've been loaded with using
+         * the key we were initialised with.
+         */
+        public byte[] GenerateSignature()
+        {
+            if (!forSigning)
+                throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation.");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+            digest.DoFinal(hash, 0);
+
+			byte[] data = DerEncode(hash);
+            return rsaEngine.ProcessBlock(data, 0, data.Length);
+        }
+
+		/**
+         * return true if the internal state represents the signature described
+         * in the passed in array.
+         */
+        public bool VerifySignature(
+			byte[] signature)
+        {
+			if (forSigning)
+				throw new InvalidOperationException("RsaDigestSigner not initialised for verification");
+
+			byte[] hash = new byte[digest.GetDigestSize()];
+			digest.DoFinal(hash, 0);
+
+			byte[] sig;
+			byte[] expected;
+
+			try
+			{
+				sig = rsaEngine.ProcessBlock(signature, 0, signature.Length);
+				expected = DerEncode(hash);
+			}
+			catch (Exception)
+			{
+				return false;
+			}
+
+			if (sig.Length == expected.Length)
+			{
+				for (int i = 0; i < sig.Length; i++)
+				{
+					if (sig[i] != expected[i])
+					{
+						return false;
+					}
+				}
+			}
+			else if (sig.Length == expected.Length - 2)  // NULL left out
+			{
+				int sigOffset = sig.Length - hash.Length - 2;
+				int expectedOffset = expected.Length - hash.Length - 2;
+
+				expected[1] -= 2;      // adjust lengths
+				expected[3] -= 2;
+
+				for (int i = 0; i < hash.Length; i++)
+				{
+					if (sig[sigOffset + i] != expected[expectedOffset + i])  // check hash
+					{
+						return false;
+					}
+				}
+
+				for (int i = 0; i < sigOffset; i++)
+				{
+					if (sig[i] != expected[i])  // check header less NULL
+					{
+						return false;
+					}
+				}
+			}
+			else
+			{
+				return false;
+			}
+
+			return true;
+        }
+
+        public void Reset()
+        {
+            digest.Reset();
+        }
+
+		private byte[] DerEncode(byte[] hash)
+		{
+			if (algId == null)
+			{
+				// For raw RSA, the DigestInfo must be prepared externally
+				return hash;
+			}
+
+			DigestInfo dInfo = new DigestInfo(algId, hash);
+
+			return dInfo.GetDerEncoded();
+		}
+    }
+}