Issue creating RSA key with small primes in C# - c#

I want to write a .net based solution to the Cicdia 2012 triange puzzle.
http://uncovering-cicada.wikia.com/wiki/The_Triangle_Puzzle
Unfortunately, I keep getting the error "bad data" when creating an RSA key with the prime numbers from the solution:
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Exponent = BigInteger.Parse("65537").ToByteArray();
RSAKeyInfo.P = BigInteger.Parse("99554414790940424414351515490472769096534141749790794321708050837").ToByteArray();
RSAKeyInfo.Q = BigInteger.Parse("104593961812606247801193807142122161186583731774511103180935025763").ToByteArray();
RSA.ImportParameters(RSAKeyInfo);
I understand the basics of asymetric encryption, but I cant understand why I cant create my own key with small primes... unless the RSACryptoServiceProvider has some kind of "strength checker" or something.

Here is an some example code illustrating my comments.
public static void Main(string[] args)
{
RSAParameters RSAKeyInfo = new RSAParameters();
BigInteger p = BigInteger.Parse("99554414790940424414351515490472769096534141749790794321708050837");
BigInteger q = BigInteger.Parse("104593961812606247801193807142122161186583731774511103180935025763");
BigInteger modulus = p * q;
BigInteger e = BigInteger.Parse("65537");
BigInteger d = BigInteger.Parse("3198894071003639550820071093788580812499328515050919260466968671765341413862337988421155590663267840745788239672194253184260553629");
BigInteger dp = d % (p - 1);
BigInteger dq = d % (q - 1);
BigInteger inverseQ = BigInteger.ModPow(q, p - 2, p);
RSAKeyInfo.Modulus = SwapEndian(modulus.ToByteArray());
RSAKeyInfo.Exponent = SwapEndian(e.ToByteArray());
RSAKeyInfo.P = SwapEndian(p.ToByteArray());
RSAKeyInfo.Q = SwapEndian(q.ToByteArray());
RSAKeyInfo.D = SwapEndian(d.ToByteArray());
RSAKeyInfo.DP = SwapEndian(dp.ToByteArray());
RSAKeyInfo.DQ = SwapEndian(dq.ToByteArray());
RSAKeyInfo.InverseQ = SwapEndian(inverseQ.ToByteArray());
RSA rsa = RSA.Create();
rsa.ImportParameters(RSAKeyInfo);
Console.WriteLine("Hello World!");
}
private static byte[] SwapEndian(byte[] v0)
{
byte[] v = (byte[])v0.Clone();
int i = 0;
int j = v.Length - 1;
while (i < j)
{
byte t = v[i];
v[i] = v[j];
v[j] = t;
i++;
j--;
}
return v;
}

Related

Bouncy Castle SHA-384withECDSA Signature Verification Giving an Exception

I am working on signature verification using BouncyCastle using C# and .NET 4.7.
I was following an answer here on SO that explained the verification process using SHA-256withECDSA here.
As per that my code for verifying signature for that example using both approaches works well as per below code fragment.
public static void Main(string[] args)
{
//SHA256 With ECDSA - Baseline that works. Values received from BC answer on SO
String sigSHA256
= "e1f5cecccedfe5228d9331098e84b69a0675cdd9ac066ecfada7fea761f52a4cde902a0abd362883127230326fb556af14e894d39a3e14437aaa4134a3476c84";
String msgSHA256 = "00000000dcb320137ddd6f825660750ab655219fad66951c64f0420be8ac902975197ed2b0da54cd3d502d34dd04c8d74b2958a0b8792ae4730df6d25a6969bcad9f93a7d6229e5a0100000017cf5242732bba21a0b0e7dad7102cf7bdb2c8d7a665045816a886d7";
String pubSHA256 = "b679e27513e2fff8fdeb54409c242776f3517f370440d26885de574a0b0e5309a9de4ea055b0bf302d9f00875f80e28cd29bb95a48aa53746d7de9465123dbb7";
VerifySHA256Bouncy(HexStringToByteArray(msgSHA256), HexStringToByteArray(sigSHA256), HexStringToByteArray(pubSHA256));
Console.ReadLine();
}
public static void VerifySHA256Bouncy(byte[] message, byte[] signature, byte[] pubkey)
{
BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());
X9ECParameters ecParams = NistNamedCurves.GetByName("P-256");
ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
var G = ecParams.G;
Org.BouncyCastle.Math.EC.ECCurve curve = ecParams.Curve;
Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);
ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
// expected format is SEQUENCE {INTEGER r, INTEGER s}
var derSignature = new DerSequence(
// first 32 bytes is "r" number
new DerInteger(new BigInteger(1, signature.Take(32).ToArray())),
// last 32 bytes is "s" number
new DerInteger(new BigInteger(1, signature.Skip(32).ToArray())))
.GetDerEncoded();
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, pubkeyParam);
verifier.BlockUpdate(message, 0, message.Length);
bool result = verifier.VerifySignature(derSignature);
Console.WriteLine("result: " + result);
}
I am trying to adapt it to my use case which is verifying a SHA-384withECDSA signature. The only difference I found was the keylengths to be different. For SHA-256withECDSA the public key length used translated to a byte array of 64. Hence in the implementation I am doing:
BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());
For SHA-384withECDSA, I have tweaked it as below.
public static void Main(string[] args)
{
//SHA384 With ECDSA - Values received from Java integration example
String sig384
= "306402304f070f3cb570f92f573385880aaa58febc06b6842be59e8f56d196c63a5aacbb7124493bee84e0331c36eb9c4e3e27db0230628c89f28a53e4c2ed089abe2ada179cc64e3eb33204b0be07cdd34bd3cd5ed4d6f0aaf380cc0d436faee15509dadc14";
String msg384 = "{\"transaction\":{\"amount\":\"64.50\",\"id\":\"248686\",\"type\":\"SALE\",\"result\":\"APPROVED\",\"card\":\"XXXXXXXXXXXX1111\",\"csc\":\"999\",\"authorization-code\":\"TAS231\",\"batch-string-id\":\"44\",\"display-message\":\"Transaction approved\",\"result-code\":\"000\",\"exp-date\":\"1218\"},\"payloadType\":\"transaction\"}";
String pub384 = "307a301406072a8648ce3d020106092b240303020801010c0362000422ffee50bdb73df2698df79b8f62fa06c005acfb5d8e92c3088053620da94eb1f8978c769ace34231b51e41394b873b07a673dfb08e14e975fb26355a639f1be4339e787390ca4c8dd6463c76bc8421457906aafa8b9981445276fde833c136b";
VerifySHA384Bouncy(Encoding.ASCII.GetBytes(msg384), HexStringToByteArray(sig384), HexStringToByteArray(pub384));
Console.ReadLine();
}
public static void VerifySHA384Bouncy(byte[] message, byte[] signature, byte[] pubkey)
{
BigInteger x = new BigInteger(1, pubkey.Take(62).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(62).ToArray());
X9ECParameters ecParams = NistNamedCurves.GetByName("P-384");
ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
var G = ecParams.G;
Org.BouncyCastle.Math.EC.ECCurve curve = ecParams.Curve;
Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);
ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
// expected format is SEQUENCE {INTEGER r, INTEGER s}
var derSignature = new DerSequence(
// first 32 bytes is "r" number
new DerInteger(new BigInteger(1, signature.Take(62).ToArray())),
// last 32 bytes is "s" number
new DerInteger(new BigInteger(1, signature.Skip(62).ToArray())))
.GetDerEncoded();
var verifier = SignerUtilities.GetSigner("SHA-384withECDSA");
verifier.Init(false, pubkeyParam);
verifier.BlockUpdate(message, 0, message.Length);
bool result = verifier.VerifySignature(derSignature);
Console.WriteLine("result: " + result);
}
The keylength for this example translates to a byte array of 124. Hence in my code I am doing
BigInteger x = new BigInteger(1, pubkey.Take(62).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(62).ToArray());
My code for verifying a SHA-384withECDSA signature throws an exception:
System.ArgumentException
HResult=0x80070057
Message=value invalid in Fp field element
Parameter name: x
Source=BouncyCastle.Crypto
StackTrace:
at Org.BouncyCastle.Math.EC.FpFieldElement..ctor(BigInteger q, BigInteger r, BigInteger x)
at Org.BouncyCastle.Math.EC.FpCurve.FromBigInteger(BigInteger x)
at Org.BouncyCastle.Math.EC.ECCurve.CreatePoint(BigInteger x, BigInteger y, Boolean withCompression)
at Org.BouncyCastle.Math.EC.ECCurve.CreatePoint(BigInteger x, BigInteger y)
at SignatureVerification.Program.VerifySHA384Bouncy(Byte[] message, Byte[] signature, Byte[] pubkey)
I am not sure if I am way off in my approach to the problem or I am comparing apples to oranges, I am not finding enough examples for SHA-384withECDSA to be able to figure this out. Any help would be greatly appreciated.
There are several issues:
An analysis with an ASN.1 Parser shows that the public key is given in X.509 format, s. e.g. here. I.e. the raw key x|y results as the last 2 * 48 = 96 bytes:
pub384 = pub384.Substring(pub384.Length - 96 * 2); // 96 * 2 due to the hex encoding
The determination of x and y coordinate is (x and y are 48 bytes each):
BigInteger x = new BigInteger(1, pubkey.Take(48).ToArray());
BigInteger y = new BigInteger(1, pubkey.Skip(48).ToArray());
Furthermore, the analysis with the ASN.1 parser reveals that the public key belongs to the curve brainpoolp384t1:
X9ECParameters ecParams = ECNamedCurveTable.GetByName("brainpoolp384t1");
Also, the signature sig384 is already specified in ASN.1/DER format and not in r|s (IEEE P1363) format, so the determination of derSignature can be omitted:
bool result = verifier.VerifySignature(signature); // true
With these changes, the signature is successfully verified.
Full code:
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Linq;
using System.Text;
...
public static void Main(string[] args)
{
string sig384 = "306402304f070f3cb570f92f573385880aaa58febc06b6842be59e8f56d196c63a5aacbb7124493bee84e0331c36eb9c4e3e27db0230628c89f28a53e4c2ed089abe2ada179cc64e3eb33204b0be07cdd34bd3cd5ed4d6f0aaf380cc0d436faee15509dadc14";
string msg384 = "{\"transaction\":{\"amount\":\"64.50\",\"id\":\"248686\",\"type\":\"SALE\",\"result\":\"APPROVED\",\"card\":\"XXXXXXXXXXXX1111\",\"csc\":\"999\",\"authorization-code\":\"TAS231\",\"batch-string-id\":\"44\",\"display-message\":\"Transaction approved\",\"result-code\":\"000\",\"exp-date\":\"1218\"},\"payloadType\":\"transaction\"}";
string pub384 = "307a301406072a8648ce3d020106092b240303020801010c0362000422ffee50bdb73df2698df79b8f62fa06c005acfb5d8e92c3088053620da94eb1f8978c769ace34231b51e41394b873b07a673dfb08e14e975fb26355a639f1be4339e787390ca4c8dd6463c76bc8421457906aafa8b9981445276fde833c136b";
pub384 = pub384.Substring(pub384.Length - 96 * 2); // Fix 1
VerifySHA384Bouncy(Encoding.ASCII.GetBytes(msg384), HexStringToByteArray(sig384), HexStringToByteArray(pub384));
}
public static void VerifySHA384Bouncy(byte[] message, byte[] signature, byte[] pubkey)
{
BigInteger x = new BigInteger(1, pubkey.Take(48).ToArray()); // Fix 2
BigInteger y = new BigInteger(1, pubkey.Skip(48).ToArray());
X9ECParameters ecParams = ECNamedCurveTable.GetByName("brainpoolp384t1"); // Fix 3
ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
ECPoint G = ecParams.G;
ECCurve curve = ecParams.Curve;
ECPoint q = curve.CreatePoint(x, y);
ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);
ISigner verifier = SignerUtilities.GetSigner("SHA-384withECDSA");
verifier.Init(false, pubkeyParam);
verifier.BlockUpdate(message, 0, message.Length);
bool result = verifier.VerifySignature(signature); // Fix 4
Console.WriteLine("result: " + result); // result: True
}
private static byte[] HexStringToByteArray(string str)
{
return Hex.Decode(str);
}

C# RSA implementation - encrypt and decrypt string

I need to implement simple RSA program in C# which will encrypt and decrypt strings (I've only found working example with integers not strings). Here's what I have:
public static BigInteger euklides(BigInteger e, BigInteger phi)
{
BigInteger x = BigInteger.Zero;
BigInteger y = BigInteger.One;
BigInteger lastx = BigInteger.One;
BigInteger lasty = BigInteger.Zero;
BigInteger temp;
BigInteger phiOriginal = phi;
while (!phi.Equals(BigInteger.Zero))
{
BigInteger q = BigInteger.Divide(e, phi);
BigInteger r = e % phi;
e = phi;
phi = r;
temp = x;
x = BigInteger.Subtract(lastx, BigInteger.Multiply(q, x));
lastx = temp;
temp = y;
y = BigInteger.Subtract(lasty, BigInteger.Multiply(q, y));
lasty = temp;
}
if (lastx.CompareTo(BigInteger.Zero) > 0)
return lastx;
else
return BigInteger.Subtract(phiOriginal, BigInteger.Negate(lastx));
}
static void Main(string[] args)
{
string p = "7980724731397402975742029729905308770673214017611336839039040369204140805514764089662732987804985392082433047940750616286743290036112256089536100737587703";
string q = "11740715087896814955393784459915361965809210593358905908497824655235476226804879681331854108325808626683143689269719764318983014066100961329905775841321093";
//Convert string to BigInteger
BigInteger rsa_p = BigInteger.Parse(p);
BigInteger rsa_q = BigInteger.Parse(q);
BigInteger rsa_n = BigInteger.Multiply(rsa_p, rsa_q);
BigInteger rsa_fn = BigInteger.Multiply((rsa_p - 1), (rsa_q - 1));
BigInteger rsa_e = 13;
BigInteger rsa_d = euklides(rsa_e, rsa_fn);
string message = "hellojs";
byte[] buffer2 = Encoding.ASCII.GetBytes(message);
BigInteger m = new BigInteger(buffer2);
BigInteger C = BigInteger.ModPow(m, rsa_e, rsa_n);
Console.WriteLine("Encrypted: " + Convert.ToBase64String(C.ToByteArray()));
BigInteger M = BigInteger.ModPow(C, rsa_d, rsa_n);
byte[] decoded2 = M.ToByteArray();
if (decoded2[0] == 0)
{
decoded2 = decoded2.Where(b => b != 0).ToArray();
}
string message3 = ASCIIEncoding.ASCII.GetString(decoded2);
Console.WriteLine("Decrypted: " + message3);
Console.ReadKey();
}
My problem is decrypting - I get some random unreadable characters. It seems something is wrong with converting string to BigInteger before encryption and BigInteger to string after decryption. Do anyone have any idea how to fix it? Thanks :)
EDIT:
For values which were hardcoded, program didn't work. But for another values like:
p 8954937300280438804244939471563100247837695704192373673128222819901439388583431335500737707702230367892764837438054304217178172911151362857419350093873829
q 11326960495280396859963951277476202902476795535605385467047045654941363025487995731611833231534039530362375614741751500907989679189162164468639573092827123
it's working now. I have no idea why :)

ECDSA get public key in C#

I can't figure this out it should be super simple.
I have both the C# and BouncyCastle (also C#) crypto libraries.
I just need to give a byte array as private key, specify the curve used and get the public key.
My curve is SEC-P-256-K1/secp256k1, but really if you can just help me navigate the sea of options and classes I don't need or care about I can set that myself.
Martin's answer seems fine, but it might be done easier:
public Tuple<byte[],byte[]> GetPublicKey(byte[] privateKey)
{
BigInteger privKeyInt = new BigInteger(+1, privateKey);
var parameters = SecNamedCurves.GetByName("secp256k1");
ECPoint qa = parameters.G.Multiply(privKeyInt);
byte[] pubKeyX = qa.X.ToBigInteger().ToByteArrayUnsigned();
byte[] pubKeyY = qa.Y.ToBigInteger().ToByteArrayUnsigned();
return Tuple.Create(pubKeyX, pubKeyY);
}
This is the solution. I was confused by the Curve constructors taking a parameter 'q' which was actually supposed to be 'p' (the prime modulus of the field).
I also don't understand why I have to do so much myself such as the point multiplication to get public key. How will others who have not read EC math know to do that?
Why is there no "GetPubKey" method!?!
Oh well I hope this helps someone. User friendly is not what BouncyCastle is about I guess.
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Crypto.Parameters;
using System.Text.RegularExpressions;
public static Tuple<byte[], byte[]> GetSecp256k1PublicKey(byte[] privateKey)
{
//Secp256k1 curve variables - https://en.bitcoin.it/wiki/Secp256k1
var privKeyInt = new BigInteger(+1, privateKey);
var a = new BigInteger("0");
var b = new BigInteger("7");
var GX = new BigInteger(+1, HexStringToByteArray("79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798"));
var GY = new BigInteger(+1, HexStringToByteArray("483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8"));
var n = new BigInteger(+1, HexStringToByteArray("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141"));
var h = new BigInteger("1");
var p = new BigInteger(+1, HexStringToByteArray("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F"));
var q = h.Multiply(n).Mod(p); //Is this right???
//- http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
ECCurve curve = new Org.BouncyCastle.Math.EC.FpCurve(p, a, b);
ECPoint G = new Org.BouncyCastle.Math.EC.FpPoint(curve, new FpFieldElement(p, GX), new FpFieldElement(p, GY));
var Qa = G.Multiply(privKeyInt);
byte[] PubKeyX = Qa.X.ToBigInteger().ToByteArrayUnsigned();
byte[] PubKeyY = Qa.Y.ToBigInteger().ToByteArrayUnsigned();
return Tuple.Create<byte[], byte[]>(PubKeyX, PubKeyY);
}
public static byte[] HexStringToByteArray(string hex)
{
if(String.IsNullOrWhiteSpace(hex))
return new byte[0];
hex = Regex.Replace(hex, "[\\s-\\{}]", "");
if (hex.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits.");
if (!Regex.IsMatch(hex, "(^|\\A)[0-9A-Fa-f]*(\\Z|$)"))
throw new Exception("Not hex.");
byte[] arr = new byte[hex.Length >> 1];
hex = hex.ToUpper();
for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
}
return arr;
}
Using Vasiliy's answer I was struggling to get the same results as this tutorial but finally got the correct results after changing his code by adding .Normalize():
public Tuple<byte[],byte[]> GetPublicKey(byte[] privateKey){
BigInteger privKeyInt = new BigInteger(+1, privateKey);
var parameters = SecNamedCurves.GetByName("secp256k1");
ECPoint qa = parameters.G.Multiply(privKeyInt).Normalize();
byte[] pubKeyX = qa.XCoord.ToBigInteger().ToByteArrayUnsigned();
byte[] pubKeyY = qa.YCoord.ToBigInteger().ToByteArrayUnsigned();
return Tuple.Create(pubKeyX, pubKeyY);
}
I'd add this as a comment but alas not enough reputation.

Generating 40 char fingerprint for DSA

Is there a existing method within .NET Framework (C#) to generate a 40 char (public?) fingerprint as shown below, when you have P, Q, G, Y and X?
Or would anybody know on how to achieve this?
Fingerprint: 81F68001 29D928AD BEE41B78 AA862106 CAEAC892
EDIT:
here is an example of what i'm trying to do:
string P = "00F35DBCD6D4C296D2FE9118B659D02608B76FAC94BB58B10283F20390E2B259BAC602466162E9EF3E6A1590702CAE49B681A75A878E266F1AFAE0FA89DA5CA44A1551B517A3F80A9D6C630F9E7D239B437F7402DF8055069735894CD9D4708F8777B5E4F3E6A8B2D4EEE50DB2C96BA16D3C81FEB923697D649A8B7771B10E5B3F";
string Q = "00B5AF039839043410E04C35BDDB30679969EBAC8B";
string G = "00F300A68E54DE33A09001E28EC09F2ABF5DAF208774F2514D878D5587D870C91C6DE42B4705078C6F4438765050039C2950B6DE85AFC0D12A7A5C521782CB760918DF68F385A7F177DF50AA6BA0284090454106E422FCAE5390ADC00B859A433430019E970BFA614374DE1FB40C600345EF19DC01A122E4676C614DC29D3DC2FE";
string Y = "00A5317849AF22BA6498F1EF973158C8BDA848BEB074CB141E629C927B18F29C8CE99815001BAAB2931F339B5C52A79BC3DCB0C5962C302707BA6FF1807EEB91D751BA723BB7512C20689AC5E67A1B656CDFD1BA2D4F6A44308509486AA8754B47784FC4C03E546897200388656BA5834A2CC0E18E58454FF60C1BA5411D6F50FD";
i'm missing the code for this intermediate piece. how do i convert P, Q, G, Y into the fingerprint. I tried different approaches, but i'm unsuccessful generating the fingerprint i see in the application that i'm trying to recreate.
/* convert public key (bigIntKey) into fingerprint */
var bigIntHash = new BigInteger(SHA1.Create().ComputeHash(key.ToByteArray()));
byte[] hash = bigIntHash.ToByteArray();
if (hash.Length != 20)
{
throw new IndexOutOfRangeException();
}
for (int i = 0; i < 5; i++)
{
int lf = BitConverter.ToInt32(hash, i * 4);
Debug.Write(lf.ToString("X") + " ");
}
EDIT2:
i tried this, but that is not working
// switch P, Q, G, Y and separately to make it work.
byte[] pArr = StringToByteArray(P);
pArr = Tools.Endian.ReverseBytes(pArr);
byte[] qArr = StringToByteArray(Q);
qArr = Tools.Endian.ReverseBytes(qArr);
byte[] gArr = StringToByteArray(G);
gArr = Tools.Endian.ReverseBytes(gArr);
byte[] yArr = StringToByteArray(Y);
yArr = Tools.Endian.ReverseBytes(yArr);
byte[] xArr = StringToByteArray(X);
xArr = Tools.Endian.ReverseBytes(xArr);
byte[] arr = Combine(pArr, qArr, gArr, yArr);
DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
DSAParameters par = new DSAParameters();
par.P = pArr;
par.Q = qArr;
par.G = gArr;
par.Y = yArr;
par.X = xArr;
dsa.ImportParameters(par);
var xml = dsa.ToXmlString(true);
It will fail on the ImportParameter.
Thank you
You need to follow the OTR spec, which says the components of the key use MPI encoding, which it specifies as the length (32 bit big-endian) followed by the integer (big-endian, no leading zeros)
void Main()
{
string P = "00F35DBCD6D4C296D2FE9118B659D02608B76FAC94BB58B10283F20390E2B259BAC602466162E9EF3E6A1590702CAE49B681A75A878E266F1AFAE0FA89DA5CA44A1551B517A3F80A9D6C630F9E7D239B437F7402DF8055069735894CD9D4708F8777B5E4F3E6A8B2D4EEE50DB2C96BA16D3C81FEB923697D649A8B7771B10E5B3F";
string Q = "00B5AF039839043410E04C35BDDB30679969EBAC8B";
string G = "00F300A68E54DE33A09001E28EC09F2ABF5DAF208774F2514D878D5587D870C91C6DE42B4705078C6F4438765050039C2950B6DE85AFC0D12A7A5C521782CB760918DF68F385A7F177DF50AA6BA0284090454106E422FCAE5390ADC00B859A433430019E970BFA614374DE1FB40C600345EF19DC01A122E4676C614DC29D3DC2FE";
string Y = "00A5317849AF22BA6498F1EF973158C8BDA848BEB074CB141E629C927B18F29C8CE99815001BAAB2931F339B5C52A79BC3DCB0C5962C302707BA6FF1807EEB91D751BA723BB7512C20689AC5E67A1B656CDFD1BA2D4F6A44308509486AA8754B47784FC4C03E546897200388656BA5834A2CC0E18E58454FF60C1BA5411D6F50FD";
var publicKey =
ToMPI(HexToBytes(P))
.Concat(ToMPI(HexToBytes(Q)))
.Concat(ToMPI(HexToBytes(G)))
.Concat(ToMPI(HexToBytes(Y)))
.ToArray();
var fingerprint=BitConverter.ToString(SHA1.Create().ComputeHash(publicKey)).Replace("-","");
fingerprint.Dump();
}
byte[] ToMPI(byte[] data)
{
//Truncate leading 0 bytes
data = data.SkipWhile(b=>b==0).ToArray();
//Length prefix - 32 bit big-endian integer
var lenBytes=new byte[4];
lenBytes[0]=(byte)(data.Length>>24);
lenBytes[1]=(byte)(data.Length>>16);
lenBytes[2]=(byte)(data.Length>>8);
lenBytes[3]=(byte)(data.Length>>0);
return lenBytes.Concat(data).ToArray();
}
// from http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
public static byte[] HexToBytes(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
MSDN mentions what you need to do your job on this page.
Then, have a look at this answer from this SO page. The accepted answer gives the following code (and I quote):
var dsa = new DSACryptoServiceProvider();
var privateKey = dsa.ExportParameters(true); // private key
var publicKey = dsa.ExportParameters(false); // public key
I think you have everything you need to get you going.
CHEERS!
I've used this class to generate a OTR DSA key:
https://github.com/mohamedmansour/OTRLib/blob/master/Src/OTR/OTRUtilities/DSASigner.cs
Make the class public and call without constructor parameters.
var signer = new DSASigner();
var _des_key_object = signer.GetDSAKeyParameters();
Later reuse of the same key:
string _dsa_key_1_p = _des_key_object.GetHexParamP();
string _dsa_key_1_q = _des_key_object.GetHexParamQ();
string _dsa_key_1_g = _des_key_object.GetHexParamG();
string _dsa_key_1_x = _des_key_object.GetHexParamX();
// This can be a JSON for storing.
var keysArray = new string[] { _dsa_key_1_p, _dsa_key_1_q, _dsa_key_1_g, _dsa_key_1_x };
_des_key_object = new DSAKeyParams(_des_key_objectJson[0], _des_key_objectJson[1], _des_key_objectJson[2], _des_key_objectJson[3]);

Use PEM encoded RSA private key in .NET

I have a private key which looks like this:
-----BEGIN RSA PRIVATE KEY-----
Some private key data
-----END RSA PRIVA
I need to use this key in my C# project, but I couldn't find any example how to use key in this format. Thanks
step 1 get "Some private key data" content.remove -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----, Removes all line symbols("\n");
step 2. parse key to RSA.
private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
{
var privateKeyBits = System.Convert.FromBase64String(privateKey);
var RSA = new RSACryptoServiceProvider();
var RSAparams = new RSAParameters();
using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
{
byte bt = 0;
ushort twobytes = 0;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
else
throw new Exception("Unexpected value read binr.ReadUInt16()");
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102)
throw new Exception("Unexpected version");
bt = binr.ReadByte();
if (bt != 0x00)
throw new Exception("Unexpected value read binr.ReadByte()");
RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
}
RSA.ImportParameters(RSAparams);
return RSA;
}
private int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02)
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte();
else
if (bt == 0x82)
{
highbyte = binr.ReadByte();
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt;
}
while (binr.ReadByte() == 0x00)
{
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
All major .NET/C# Cryptography libraries (like BouncyCastle, or SecureBlackbox [commercial]) should support this format, as well as operations with loaded key (encryption/decryption/signing/verification).
Though an older post I though I'd glue my own answer to this question as I bumped into the same challenge earlier this year.
I wrote a library for the handling of PEM keys, encryption, decryption, signing and signature verification.
There's an complete example solution attached to it (Load-Encrypt-Decrypt-Save), so you should be able to be up running in no-time.
https://github.com/jrnker/CSharp-easy-RSA-PEM
Cheers,
Christoffer
The following article I wrote about digital signature gives a solution in c# (no additional library needed). The code shows how a RSA private key in PEM format can be converted to the XML format used by the .NET RSACryptoServiceProvider Class.
With Atlando Crypto Currency Geo Service your identity is stored in your browser after registration. At each request a contract with us is signed and encrypted by this identity with your private key. This article explains how it works.
Code below gives an implementation in C# (RSACryptoServiceProvider Class) of the authentication process through comparison of the original and a signed version. The modulus comes from the RSA public key in PEM format (exponent AQAB).
private static bool Verify(string original, string signature, string modulus)
{
SHA256Managed sha = new SHA256Managed();
byte[] bytes = Encoding.UTF8.GetBytes(original);
byte[] hash = sha.ComputeHash(bytes);
sha.Clear();
byte[] signed = new byte[signature.Length/2];
for (int i = 0; i < signature.Length; i += 2)
{
signed[i/2] = Convert.ToByte(Convert.ToInt32(signature.Substring(i, 2), 16));
}
string key = "<RSAKeyValue><Modulus>" + modulus + "</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(key);
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
RSADeformatter.SetHashAlgorithm("SHA256");
return RSADeformatter.VerifySignature(hash, signed);
}
}
public static string Modulus(string pem)
{
byte[] x509der = Convert.FromBase64String(pem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", ""));
byte[] seqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509der);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30) ReadASNLength(reader); //skip the size
else return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30) identifierSize = ReadASNLength(reader);
else return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(seqOID) == false) return null; //is the object identifier rsaEncryption PKCS#1?
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
rsa.ImportParameters(RSAKeyInfo);
return rsa.ToXmlString(false).Replace("<RSAKeyValue><Modulus>", "").Replace("</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>", "");
}
}
}
}
return null;
}
public static int ReadASNLength(BinaryReader reader)
{//Note: this method only reads lengths up to 4 bytes long as this is satisfactory for the majority of situations.
int length = reader.ReadByte();
if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
{
int count = length & 0x0000000f;
byte[] lengthBytes = new byte[4];
reader.Read(lengthBytes, 4 - count, count);
Array.Reverse(lengthBytes); //
length = BitConverter.ToInt32(lengthBytes, 0);
}
return length;
}
First, you need to transform the private key to the form of RSA parameters using Bouncy Castle library. Then you need to pass the RSA parameters to the RSA algorithm as the private key. Lastly, you use the JWT library to encode and sign the token.
public string GenerateJWTToken(string rsaPrivateKey)
{
var rsaParams = GetRsaParameters(rsaPrivateKey);
var encoder = GetRS256JWTEncoder(rsaParams);
// create the payload according to your need
var payload = new Dictionary<string, object>
{
{ "iss", ""},
{ "sub", "" },
// and other key-values
};
var token = encoder.Encode(payload, new byte[0]);
return token;
}
private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
{
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaParams);
var algorithm = new RS256Algorithm(csp, csp);
var serializer = new JsonNetSerializer();
var urlEncoder = new JwtBase64UrlEncoder();
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
return encoder;
}
private static RSAParameters GetRsaParameters(string rsaPrivateKey)
{
var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
using (var ms = new MemoryStream(byteArray))
{
using (var sr = new StreamReader(ms))
{
// use Bouncy Castle to convert the private key to RSA parameters
var pemReader = new PemReader(sr);
var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
}
}
}
ps: the RSA private key should have the following format:
-----BEGIN RSA PRIVATE KEY-----
{base64 formatted value}
-----END RSA PRIVATE KEY-----

Categories