A blind signature allows Bob to hide the content of a message before it is signed by a trusted entity (the signer). This is typically used when the creator of a message is different to the entity which signs it. For example, Bob may blind the message (such as his vote), and then for Trent to sign it as being valid, but where Trent will not know the contents of the message (or his vote). In this case we will use the Bouncy Castle library to implement a blind RSA signature for RSA-512, RSA-1024 and RSA-1536 (Note: in production we would create use RSA-2048 or RSA-4096).
RSA Blinded Signatures using Bouncy Castle and C# |
Outline
Let’s see if we can use a carbon copy to allow Bob to cast a trusted vote in an election. First, Bob makes his vote on a piece of paper and then puts it into a special envelope. He gives this envelope to Peggy, and who seals it. But, there is a special place for Peggy to sign on the envelope, which has a carbon copy strip. When she signs the outside of the envelope, her signature is copied to the vote inside the envelope. So, when Victor opens up the envelope, he examines Bob’s vote and can see if it has been signed by Peggy, and so he trusts the vote. Overall, Peggy never gets to see Bob’s vote but can sign for it.
In a digital form, Peggy (the “prover”) creates a key pair: a public key and a private key. She will sign with her private key and then prove her signature with her public key.
With a blinded signature, Bob creates a blinded version of his message for Peggy to sign with her private key. She thus cannot see what the message is that she is signing. Then, Bob will unblind the message and send the signature to Victor (the “verifier”), and who will verify it with Peggy’s public key.
Method
A blind signature allows Bob to hide the content of a message before it is signed by a trusted entity (the signer). This is typically used when the creator of a message is different to the entity which signs it. For example, Bob may blind the message (such as his vote), and then for Trent to sign it as being valid, but where Trent will not know the contents of the message (or his vote). This could thus be applied to voting systems, and where Bob registers his vote, and Trent then signs it as being a valid vote cast (using Trent's signing key). Bob can then get this back, and unblind his message, and then cast the vote. The vote will thus contain Bob's message, but be signed by Trent.
In traditional RSA, we sign with:
\(Sig=M^d \pmod N\)
and where \(N\) is the modulus, and \(d\) is the decryption exponent. In a blinded version, we generate a random value of \(r\) and which is relatively prime to N (\(gcd(r, N) = 1)\)). We then compute:
\(m'=m.r^e\)
The value of \(m'\) is then sent to the signing authority (Trent). The signing authority then computes the blinded signature as:
\(s'=(m')^d \pmod N\)
This is then sent back to the creator of the message, and who can then sign with:
\(s=(s').r^{-1} \pmod N\)
This will remove the blinding factor, and now be signed by the signing authority, but will not reveal the message to them. Overall this will work because:
\(s=(s').r^{-1} = (m')^d.r^{-1} = (m.r^e)^d.r^{-1} = m^d.r^{ed}.r^{-1} = m^d.r.r^{-1} = m^d \pmod N\)
Thus we have a signature which is signed by Trent's decryption key (\(d\)), and with the message of Bob (\(m\)).
Coding
We can create an RSA key pair with the Bouncy Castle library with:
RsaKeyPairGenerator pGen = new RsaKeyPairGenerator(); pGen.Init(new KeyGenerationParameters(new SecureRandom(), s)); var pair = pGen.GenerateKeyPair();
Next we can generate the blinding factor:
// "Blind" the signature PssSigner signer = new PssSigner(new RsaBlindingEngine(), new Sha256Digest(), 20); signer.Init(true, blindingParams); signer.BlockUpdate(data, 0, data.Length); byte[] sig = signer.GenerateSignature(); // get signature ready to sign
Next we can get the signer to sign the blinded message:
// Sign the blinded message RsaEngine engine = new RsaEngine(); engine.Init(true, pair.Private); var blindsign = engine.ProcessBlock(sig, 0, sig.Length);
Finally, we can then unblind the signature:
// Unblind signature RsaBlindingEngine blindingEngine = new RsaBlindingEngine(); blindingEngine.Init(false, blindingParams); byte[] signew = blindingEngine.ProcessBlock(blindsign, 0, blindsign.Length);
Finally, we can test the signature with the public key:
// Verify signature signer = new PssSigner(new RsaEngine(), new Sha256Digest(), 20); signer.Init(false,pair.Public); signer.BlockUpdate(data, 0,data.Length); var rtn=signer.VerifySignature(signew);
In this blindsign is the blinded signature, and signnew is the unblinded version.
Code
First we install the Bouncy Castle library:
dotnet add package BouncyCastle.Cryptography
Next some code:
namespace Blinded { using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Generators; using System.Text; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Signers; class Program { public static byte[] getRandomBytes(int count) { byte[] bytes = new byte[count]; new SecureRandom().NextBytes(bytes); return bytes; } static void Main(string[] args) { string size="512"; string str="Hello"; if (args.Length >0) str=args[0]; if (args.Length >1) size=args[1]; int s = Convert.ToInt32(size); try { byte[] data = Encoding.ASCII.GetBytes(str); RsaKeyPairGenerator pGen = new RsaKeyPairGenerator(); pGen.Init(new KeyGenerationParameters(new SecureRandom(), s)); var pair = pGen.GenerateKeyPair(); // Generate a blinding factor for public key var blindingFactorGenerator = new RsaBlindingFactorGenerator(); blindingFactorGenerator.Init((RsaKeyParameters)pair.Public); var blindingFactor = blindingFactorGenerator.GenerateBlindingFactor(); var blindingParams = new RsaBlindingParameters((RsaKeyParameters)pair.Public, blindingFactor); // "Blind" the signature PssSigner signer = new PssSigner(new RsaBlindingEngine(), new Sha256Digest(), 20); signer.Init(true, blindingParams); signer.BlockUpdate(data, 0, data.Length); byte[] sig = signer.GenerateSignature(); // get signature ready to sign // Sign the blinded message RsaEngine engine = new RsaEngine(); engine.Init(true, pair.Private); var blindsign = engine.ProcessBlock(sig, 0, sig.Length); // Unblind signature RsaBlindingEngine blindingEngine = new RsaBlindingEngine(); blindingEngine.Init(false, blindingParams); byte[] signew = blindingEngine.ProcessBlock(blindsign, 0, blindsign.Length); // Verify signature signer = new PssSigner(new RsaEngine(), new Sha256Digest(), 20); signer.Init(false,pair.Public); signer.BlockUpdate(data, 0,data.Length); var rtn=signer.VerifySignature(signew); RsaPrivateCrtKeyParameters priv = ((RsaPrivateCrtKeyParameters)pair.Private); Console.WriteLine("Message:\t{0}",str); Console.WriteLine("Key size:\t{0}",size); Console.WriteLine("RSA N:\t{0}",priv.Modulus); Console.WriteLine("RSA d:\t{0}",priv.Exponent); Console.WriteLine(" p:\t{0}",priv.P); Console.WriteLine(" q:\t{0}",priv.Q); Console.WriteLine("RSA e:\t{0}",priv.PublicExponent); Console.WriteLine("\nVerified: {0}",rtn); Console.WriteLine("\nBlinded signature: {0}",Convert.ToHexString(sig)); Console.WriteLine("\nUnblinded signature: {0}",Convert.ToHexString(signew)); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
A sample run with 512-bit RSA keys is:
Message: Hello Key size: 512 RSA N: 9501414863243560066341462321471935054349724589264480077004243951542388503653727344471281608010109011697337788541834467710255966249029373477947190794335749 RSA d: 2101381780296378860515225378314157844351619437250621884983108991190184936237638580115285913901635086465261269564456312666972810119721146925213265877744977 p: 103843561217129126640162913737503047288949391266673123278871357435590829952973 q: 91497390419583276758809687817207537680958433833057531824198623481567481042713 RSA e: 65537 Verified: True Blinded signature: 620E2C25B31A7C00C1215A794745FB932C033BB3F862C88DF8824A708E4578C50DC2AB1E8EA3861A061D0AE58A817AFC6354C03779E9452666D907F597833688 Unblinded signature: 89BF01FAFB54F1D3FD6E8A268C440C734ADA5278EC8B57AEE1820549F04EF211DDFBBCC50955CA7BD9219327B4F9C8FCEBBD27955BA26B9D94B33BDC08C2C79F
and for 1,024-bit keys:
Message: Hello Key size: 1024 RSA N: 139548682571491209896930381437672113042327208449829748322514290538687189078048164988981349592530370189327510154933510422562083105153152344607444107456757376117862645349423738671566497248108552420745388212422766902684923943063663057955365608366820150436711243094876173507265370018871914636913598338441132778809 RSA d: 27824776988616527844242759959057941882915159320966022799402711317489278472883934205006542652248205020813238917704405869155286646880217560510212183563348592431663079267637773281805655476216223401473610184553107897844201311608739306238248205511135675309880115197142940933925989320969649061165998344980227977023 p: 13032765743864262240243994099275033179906162342609640366925766837328628482061206314284306553582177344756639850907062764127697420891236779960986996883068299 q: 10707526346599898307490537981065114199140764335775726189355068810061238169486289049591166958838947438947930046738035340533530298324680283931322366828556491 RSA e: 65537 Verified: True Blinded signature: 01B299653C0B4C0A952464BD26DD8833401DD02B688B9DB5240D844BCBB1048127BA9702D4CA51858A7286181CFABDA67C6BAAB40423372BD2BFB7041013BDEE50CFB7E7916A73B95574599857DB406EA6F6BFB2A39A75B83497CB08C128DD4BB63D31D0763A73C7A4702984B2A4148B2D974A93F57593481C328E5A5AEC604C Unblinded signature: 24A4C3F4430E6D6DC715E32976324BA918A97D5444331855892AC47499F745D60C3A5B6E1A840CDFD860E17371843DF5706CAD76D3668D1481605F0D4180290AD801D338B77728890F53F4BEA83BB2128CD43EC4181BA13E16A170F1D5DD94ED8F4A706AF5A6B3A407300AA82A308EAF927294F7955885677A4E559022B40525