Following apple SKADNetwork
I've generated private key and public key with this command
openssl ecparam -name prime192v1 -genkey -noout -out private_key.pem
openssl ec -in private_key.pem -pubout -out public_key.pem
i'm able to generate a valid signature with python following this project
https://github.com/singular-labs/Singular-SKAdNetwork-App/tree/master/skadnetwork-server
But when i'm trying to generate signature via c# bouncycastle library i have different results if i use same data.
BOUNCYCASTLE TEST
public static AsymmetricCipherKeyPair ReadAsymmetricPrivateKeyParameter(string pemFilename)
{
var fileStream = System.IO.File.OpenText(pemFilename);
var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(fileStream);
var KeyParameter = (AsymmetricCipherKeyPair)pemReader.ReadObject();
return (AsymmetricCipherKeyPair)KeyParameter;
}
static void Main(string[] args)
{
AsymmetricCipherKeyPair pkey = ReadAsymmetricPrivateKeyParameter("private_key.pem");
string pars = getParamsSignature("2.0", "xxxxxxx.skadnetwork", "10", "302584613", "0a97ad57-87d1-49e7-b166-9152c708251b", "1613749507007", "0");
Sign(pkey, "", pars);
Console.Read();
}
static string getParamsSignature(string version, string ad_network_id, string campaign_id, string target_app_id, string nonce, string timestamp, string source_app_id)
{
string[] p = new string[] {
version,
ad_network_id,
campaign_id,
target_app_id,
nonce,
source_app_id,
timestamp
};
return string.Join("\u2063", p);
}
public static bool Sign(AsymmetricCipherKeyPair pubKey, string msg)
{
try
{
ECDomainParameters aa;
byte[] msgBytes = Encoding.UTF8.GetBytes(msg);
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(true, pubKey.Private);
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
byte[] sigBsdytes = signer.GenerateSignature();
var signature = Convert.ToBase64String(sigBsdytes);
//MDUCGQDgqw1YQN/vvHTxXXTpovNYUnACzkFrXJwCGCXAnr3TUbbqIUr6cBamymrypcQET5RR7Q==
}
catch (Exception exc)
{
Console.WriteLine("Verification failed with the error: " + exc.ToString());
return false;
}
return false;
}
.NET CORE 3 TEST
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
string pars = getParamsSignature("2.0", "xxxxx.skadnetwork", "10", "302584613", "0a97ad57-87d1-49e7-b166-9152c708251b", "1613749507007", "0");
//bool aaaaa = VerifySignature(aa, attribution_signature, pars);
string eccPem = File.ReadAllText("private_key.pem");
var key = ECDsa.Create(ECCurve.CreateFromValue("1.2.840.10045.3.1.1"));
key.ImportECPrivateKey(Convert.FromBase64String(eccPem), out _);
var signed = key.SignData(Encoding.UTF8.GetBytes(pars), HashAlgorithmName.SHA256);
var hash = Convert.ToBase64String(signed);
}
With .net.core test the result is even worst as the token change at each execution. I think there's a random behavior.
#BOUNCYCASTLE DETERMINISTIC TEST FORMAT R|S and ASN1
byte[] msgBytes = Encoding.UTF8.GetBytes(msg);
ECDsaSigner signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));
signer.Init(true, pubKey.Private);
var sigBsdytes = signer.GenerateSignature(msgBytes);
//SIGNATURE (R|S)
Console.WriteLine( Convert.ToBase64String(sigBsdytes.SelectMany(a => a.ToByteArray()).ToArray()));
//SIGNATURE (ASN1)
var s = new MemoryStream();
try
{
DerSequenceGenerator seq = new DerSequenceGenerator(s);
seq.AddObject(new DerInteger(sigBsdytes[0]));
seq.AddObject(new DerInteger(sigBsdytes[1]));
seq.Close();
var signature = Convert.ToBase64String(s.ToArray());
Console.WriteLine(signature);
}
catch (IOException e)
{
}
Still having a different result from python FastECDSA library if the input string is the same one.
UPDATE SOLUTION: thanks to #topaco comment
var crypt = new System.Security.Cryptography.SHA256Managed();
byte[] msgBytes = crypt.ComputeHash(Encoding.UTF8.GetBytes(msg));
ECDsaSigner signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));
signer.Init(true, pubKey.Private);
var sigBsdytes = signer.GenerateSignature(msgBytes);
//SIGNATURE (ASN1)
var s = new MemoryStream();
try
{
DerSequenceGenerator seq = new DerSequenceGenerator(s);
seq.AddObject(new DerInteger(sigBsdytes[0]));
seq.AddObject(new DerInteger(sigBsdytes[1]));
seq.Close();
var signature = Convert.ToBase64String(s.ToArray());
Console.WriteLine(signature);
}
catch (IOException e)
{
}
Related
I am trying to decrypt and validate the hash but at times of decrypting, it throws me the error 'Key does not exist' and at the time of validating the hash return False
https://payvyne.readme.io/docs/webhooks
Signature:
HEjoCsghC9X0slrE2DprptDLYdoA7jaw4Jl7vpJVxzx9GNJEiO3pYGLDPhLmVqk98QJJ/FuiS5J+fvp+msr3Y8aFzKqjRQXj5TBELT38N+A7I8y3Vc0mgeR0aDMx7I83yhfkcoyhdiGJibzqQ5SYFZ0nnEVHYXheLUlga45yg/McDICtMm6lhnrPWEuHzoZTQkhsrLN/1W1PtLjJ2DickWB78PmhpeflL2Cpe6qS3qCclqFGZ7HIl9OoxU4WXpTYgxw7eixAKB7apFdFqea4BnGravfENNl97pOBuU6fRof4KtMczVagQw3QnxFD3BBtpTepRaT+jHY8wStXUG1bxllH32WiA9CVcpY4mxKhpxzQ8YD0b+3OgkpzZYS+BVVAdVazMJeEAw7v/zaxpjbR+Zo5l9vOLdyatwM75qpwMoKnMeKJHeRytEOK54al49OHiaE+v1OkOhJA0zh5nLzEIZanIdf+hXHDz3Euecs/p0cABiFNmhzYY5fl8qEytK6j2CjXQOYgljG5dqPm7M9CW36ntZTDaIEVWql3jdi9frxc4/82w1jhROFL0pBG1zz8nimAEesB1AaxmNqW7BIxULweX7eaReeo/dIqDSbmFuT+TikPQo4XRtmpDqO37Y9P6q7ZXtHOFopSaykHUHs+NgrKlBJMM5ADg5bHWm2Qows=
Public key:
pA6ULfXWrIMq-qvxn_0CykoStq0ZMYm63lHsuXTsE4q4tgekLJDW2Lnf35ilbFU_vybBdyeJAphpsYc4P0eJBt_z2T62HAV3gnwp_GU6hWIo8faK31TSXIrLmGjZlAVynAxjFYZoNxMeZuwEXpxG4bRGs58P7XSx1fAzedX6oGIlcSLljKH4I1BHt6gJhPIHYNXQzq_a0hX54C1m1VDVP_kot8ui1YKZil_riROK_Xk4ktnOTAqXo9z4uNBqzzH2k0J2YNiCb8VOdbp7kjmH9sPLI-jb-ociy0wSkGZc1e8saGIkkSm4eUASvX_M_TTDD99OrgoIS2Vx07Tw4lK5yd28EMVBUzy2OypuPVf9PyoDGv_4241x5PpJsA9IKocD7AgwxJ3E7FBFhvuSP8c5wspkbQxBwv5nnk2zAxuZsiJeK0o3JSxjkZJEkeVY4mA3VV9SvSXEKAFg2h9J3CR9PTwrZoVBruycVtWJ4it5jroXff-aGlLoRAO0g3gtfjkJb3tw6SJTFOA49iJci76Mj8Adz3eeEEGxTxfDzh_lq0jXxTk7cQSaR2_ChYLHaoorrrFmAvWgDH_lSvlISIgey-SzUoJM9RAy4gVFdmg-XCQQlpMh_d1-IACO3EfBvYKWE-6uGIqx1nZhn9WIDdSqMp6940xRxl0vQy8vYCQ5q8U
Data for Sign in string:
{"type":"PAYMENT_STATUS_CHANGE","paymentId":"1c6e834f074ec941","status":"FAILED","timestamp":1652688286662,"amount":"164.69","currency":"GBP","description":"This is test payment","paymentType":"ONE_OFF","bankName":"Diamond bank","destinationAccount":"GBP2","createdAt":"2022-05-16T08:04:32.994","updatedAt":"2022-05-16T08:04:46.662","customerReference":"1199","refundedAmount":"0.00"}
Expo (exponent):
AQAB
Below is the code to Decrypt the signature using public key.
public static void DecryptUsingPublicKey(string publicKey, string expo, string signature)
{
var modulus = ConvertToBase64(publicKey);
var exponent = Convert.FromBase64String(expo);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(2048);
var _publicKey = csp.ExportParameters(false);
_publicKey.Modulus = modulus;
_publicKey.Exponent = exponent;
csp.ImportParameters(_publicKey);
var dataBytes = ConvertToBase64(signature);
var plainText = csp.Decrypt(dataBytes, false);
var returnData = Encoding.Unicode.GetString(plainText);
Console.WriteLine($"value: {returnData}");
}
Below is the code for Verify signature using public key
public static void VerifySignature(string signature, string pKey, string dataForSign)
{
string pKeyNew = pKey;
pKeyNew = pKeyNew.Replace("_", "/").Replace("-", "+");
string publicKey = $"<RSAKeyValue><Modulus>{pKeyNew}==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var encoder = new UTF8Encoding();
byte[] dataForSignAsBytes = encoder.GetBytes(dataForSign);
byte[] signatureAsBytes = ConvertToBase64(signature);
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider();
rsaCryptoServiceProvider.FromXmlString(publicKey);
var hashData = SHA256.Create().ComputeHash(dataForSignAsBytes);
var result1 = rsaCryptoServiceProvider.VerifyData(dataForSignAsBytes, CryptoConfig.MapNameToOID("SHA256"), signatureAsBytes);
var result2 = rsaCryptoServiceProvider.VerifyHash(hashData, CryptoConfig.MapNameToOID("SHA256"), signatureAsBytes);
var result3 = rsaCryptoServiceProvider.VerifyHash(hashData, signatureAsBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
var result4 = rsaCryptoServiceProvider.VerifyData(dataForSignAsBytes, signatureAsBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(result1);
Console.WriteLine(result2);
Console.WriteLine(result3);
Console.WriteLine(result4);
}
ConvertToBase64 function
public static byte[] ConvertToBase64(string data)
{
byte[] cyperBuffer;
string dataNew = data;
dataNew = dataNew.Replace("_", "/").Replace("-", "+");
try
{
if (dataNew.Substring(dataNew.Length - 1) != "=")
{
dataNew += "=";
}
cyperBuffer = Convert.FromBase64String(dataNew);
}
catch
{
dataNew += "=";
try
{
cyperBuffer = Convert.FromBase64String(dataNew);
}
catch
{
//If any error occured while convert to base64 then append '=' at the end.
dataNew += "=";
cyperBuffer = Convert.FromBase64String(dataNew);
}
}
return cyperBuffer;
}
This is a conversion mistake; you need to decode the base 64 signature, not encode the signature, so the following line is wrong:
byte[] signatureAsBytes = ConvertToBase64(signature);
it should be something like:
byte[] signatureAsBytes = ConvertFromBase64(signature);
Decryption is modular exponentiation with a private key. Furthermore, encryption normally uses a different padding scheme than signature generation, so you'd expect that the unpadding would fail if you try and decrypt. Only verification is possible.
I need to perform RSA decryption with public key. Following is my code and it's returning junk values after decryption.
Encryption code
public static string EncryptWithPrivate(byte[] bytes)
{
AsymmetricKeyParameter privatekey = null;
using (var reader = File.OpenText(Path.Combine(Startup.Root, $"Certificates\\private.pem")))
{
var keypair = new PemReader(reader).ReadObject() as AsymmetricCipherKeyPair;
privatekey = keypair.Private;
}
try
{
var engine = new Pkcs1Encoding(new RsaEngine());
engine.Init(true, privatekey);
var encryptedBytes = engine.ProcessBlock(bytes, 0, bytes.Length);
if (encryptedBytes.Length > 0)
{
var encryptedString = Convert.ToBase64String(encryptedBytes);
return encryptedString;
}
}
catch (Exception ex)
{
ex.Log();
return ex.Message;
}
return string.Empty;
}
Decryption Code
public static string DecryptWithPublic(byte[] bytes)
{
AsymmetricKeyParameter publicKey = null;
using (var privateKeyTextReader = new StringReader(File.ReadAllText(Path.Combine(Startup.Root, $"Certificates\\public.pem"))))
{
publicKey = (AsymmetricKeyParameter)new PemReader(privateKeyTextReader).ReadObject();
}
try
{
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
decryptEngine.Init(false, publicKey);
return Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytes, 0, bytes.Length));
}
catch (Exception ex)
{
ex.Log();
}
return string.Empty;
}
Test string: "12345678"
Output after decryption:
What am I doing wrong here?
I am trying to sign and verify the singed data in C# using Bouncy Castle. I went through almost everything I found on google but it does not seem to solve my problem. Please have a look at my approach.
public class Crypto2
{
private static RsaKeyParameters MakeKey(String modulusHexString, String exponentHexString, bool isPrivateKey)
{
var modulus = new Org.BouncyCastle.Math.BigInteger(modulusHexString);
var exponent = new Org.BouncyCastle.Math.BigInteger(exponentHexString);
return new RsaKeyParameters(isPrivateKey, modulus, exponent);
}
public static string Sign(string data, string privateModulusHexString, string privateExponentHexString)
{
/* Make the key */
RsaKeyParameters key = MakeKey(privateModulusHexString, privateExponentHexString, true);
/* Init alg */
ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
sig.Init(true, key);
/* Get the bytes to be signed from the string */
var bytes = Encoding.UTF8.GetBytes(data);
/* Calc the signature */
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
/* Base 64 encode the sig so its 8-bit clean */
var signedString = Convert.ToBase64String(signature);
return signedString;
}
public static bool Verify(string data, string expectedSignature, string publicModulusHexString, string publicExponentHexString)
{
/* Make the key */
RsaKeyParameters key = MakeKey(publicModulusHexString, publicExponentHexString, false);
/* Init alg */
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
signer.Init(false, key);
/* Get the signature into bytes */
var expectedSig = Convert.FromBase64String(expectedSignature);
/* Get the bytes to be signed from the string */
var msgBytes = Encoding.UTF8.GetBytes(data);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
return signer.VerifySignature(expectedSig);
}
protected void Page_Load(object sender, EventArgs e)
{
string data = "Hello World";
string privateKeyPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/e-MOne-i.key");
string publicKeyPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/m1pay-fpx.cer");
var key = readPrivateKey(privateKeyPath);
var publicKey = ReadCertificate(publicKeyPath);
var SignedData = Crypto2.Sign(data, ((RsaKeyParameters)key.Private).Modulus.ToString(), ((RsaKeyParameters)key.Private).Exponent.ToString());
bool result = Crypto2.Verify(data, SignedData, ((RsaKeyParameters)publicKey.GetPublicKey()).Modulus.ToString(), ((RsaKeyParameters)publicKey.GetPublicKey()).Exponent.ToString());
}
static AsymmetricCipherKeyPair readPrivateKey(string privateKeyFileName)
{
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(privateKeyFileName))
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
return keyPair;
}
static X509Certificate ReadCertificate(string filename)
{
X509CertificateParser certParser = new X509CertificateParser();
Stream stream = new FileStream(filename, FileMode.Open);
X509Certificate cert = certParser.ReadCertificate(stream);
stream.Close();
return cert;
}
}
I am running below code to get public and private key only, but it seems it outputs the whole XML format. I only need to output the keys as shown in Public and Private Key demo
static RSACryptoServiceProvider rsa;
private RSAParameters _privateKey;
private RSAParameters _publicKey;
public RSACrypto()
{
rsa = new RSACryptoServiceProvider(2048);
_privateKey = rsa.ExportParameters(true);
_publicKey = rsa.ExportParameters(false);
}
public string GetPublicKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _publicKey);
return sw.ToString();
}
public string GetPrivateKeyString()
{
var sw = new StringWriter();
var xs = new XmlSerializer(typeof(RSAParameters));
xs.Serialize(sw, _privateKey);
return sw.ToString();
}
Starting in .NET Core 3.0, this is (largely) built-in.
Writing SubjectPublicKeyInfo and RSAPrivateKey
.NET Core 3.0 built-in API
The output of the builtin API is the binary representation, to make them PEM you need to output the header, footer, and base64:
private static string MakePem(byte[] ber, string header)
{
StringBuilder builder = new StringBuilder("-----BEGIN ");
builder.Append(header);
builder.AppendLine("-----");
string base64 = Convert.ToBase64String(ber);
int offset = 0;
const int LineLength = 64;
while (offset < base64.Length)
{
int lineEnd = Math.Min(offset + LineLength, base64.Length);
builder.AppendLine(base64.Substring(offset, lineEnd - offset));
offset = lineEnd;
}
builder.Append("-----END ");
builder.Append(header);
builder.AppendLine("-----");
return builder.ToString();
}
So to produce the strings:
string publicKey = MakePem(rsa.ExportSubjectPublicKeyInfo(), "PUBLIC KEY");
string privateKey = MakePem(rsa.ExportRSAPrivateKey(), "RSA PRIVATE KEY");
Semi-manually
If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 writer package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).
To make the public key:
private static string ToSubjectPublicKeyInfo(RSA rsa)
{
RSAParameters rsaParameters = rsa.ExportParameters(false);
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
writer.PushSequence();
writer.PushSequence();
writer.WriteObjectIdentifier("1.2.840.113549.1.1.1");
writer.WriteNull();
writer.PopSequence();
AsnWriter innerWriter = new AsnWriter(AsnEncodingRules.DER);
innerWriter.PushSequence();
WriteRSAParameter(innerWriter, rsaParameters.Modulus);
WriteRSAParameter(innerWriter, rsaParameters.Exponent);
innerWriter.PopSequence();
writer.WriteBitString(innerWriter.Encode());
writer.PopSequence();
return MakePem(writer.Encode(), "PUBLIC KEY");
}
And to make the private key:
private static string ToRSAPrivateKey(RSA rsa)
{
RSAParameters rsaParameters = rsa.ExportParameters(true);
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
writer.PushSequence();
writer.WriteInteger(0);
WriteRSAParameter(writer, rsaParameters.Modulus);
WriteRSAParameter(writer, rsaParameters.Exponent);
WriteRSAParameter(writer, rsaParameters.D);
WriteRSAParameter(writer, rsaParameters.P);
WriteRSAParameter(writer, rsaParameters.Q);
WriteRSAParameter(writer, rsaParameters.DP);
WriteRSAParameter(writer, rsaParameters.DQ);
WriteRSAParameter(writer, rsaParameters.InverseQ);
writer.PopSequence();
return MakePem(writer.Encode(), "RSA PRIVATE KEY");
}
Reading them back
.NET Core 3.0 built-in API
Except that .NET Core 3.0 doesn't understand PEM encoding, so you have to do PEM->binary yourself:
private const string RsaPrivateKey = "RSA PRIVATE KEY";
private const string SubjectPublicKeyInfo = "PUBLIC KEY";
private static byte[] PemToBer(string pem, string header)
{
// Technically these should include a newline at the end,
// and either newline-or-beginning-of-data at the beginning.
string begin = $"-----BEGIN {header}-----";
string end = $"-----END {header}-----";
int beginIdx = pem.IndexOf(begin);
int base64Start = beginIdx + begin.Length;
int endIdx = pem.IndexOf(end, base64Start);
return Convert.FromBase64String(pem.Substring(base64Start, endIdx - base64Start));
}
Once that's done you can now load the keys:
using (RSA rsa = RSA.Create())
{
rsa.ImportRSAPrivateKey(PemToBer(pemPrivateKey, RsaPrivateKey), out _);
...
}
using (RSA rsa = RSA.Create())
{
rsa.ImportSubjectPublicKeyInfo(PemToBer(pemPublicKey, SubjectPublicKeyInfo), out _);
...
}
Semi-manually
If you can't use .NET Core 3.0, but you can use pre-release NuGet packages, you can make use of the prototype ASN.1 reader package (which is the same code that's used internally in .NET Core 3.0; it's just that the API surface isn't finalized).
For the public key:
private static RSA FromSubjectPublicKeyInfo(string pem)
{
AsnReader reader = new AsnReader(PemToBer(pem, SubjectPublicKeyInfo), AsnEncodingRules.DER);
AsnReader spki = reader.ReadSequence();
reader.ThrowIfNotEmpty();
AsnReader algorithmId = spki.ReadSequence();
if (algorithmId.ReadObjectIdentifierAsString() != "1.2.840.113549.1.1.1")
{
throw new InvalidOperationException();
}
algorithmId.ReadNull();
algorithmId.ThrowIfNotEmpty();
AsnReader rsaPublicKey = spki.ReadSequence();
RSAParameters rsaParameters = new RSAParameters
{
Modulus = ReadNormalizedInteger(rsaPublicKey),
Exponent = ReadNormalizedInteger(rsaPublicKey),
};
rsaPublicKey.ThrowIfNotEmpty();
RSA rsa = RSA.Create();
rsa.ImportParameters(rsaParameters);
return rsa;
}
private static byte[] ReadNormalizedInteger(AsnReader reader)
{
ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
ReadOnlySpan<byte> span = memory.Span;
if (span[0] == 0)
{
span = span.Slice(1);
}
return span.ToArray();
}
And because the private key values have to have the correct size arrays, the private key one is just a little trickier:
private static RSA FromRSAPrivateKey(string pem)
{
AsnReader reader = new AsnReader(PemToBer(pem, RsaPrivateKey), AsnEncodingRules.DER);
AsnReader rsaPrivateKey = reader.ReadSequence();
reader.ThrowIfNotEmpty();
if (!rsaPrivateKey.TryReadInt32(out int version) || version != 0)
{
throw new InvalidOperationException();
}
byte[] modulus = ReadNormalizedInteger(rsaPrivateKey);
int halfModulusLen = (modulus.Length + 1) / 2;
RSAParameters rsaParameters = new RSAParameters
{
Modulus = modulus,
Exponent = ReadNormalizedInteger(rsaPrivateKey),
D = ReadNormalizedInteger(rsaPrivateKey, modulus.Length),
P = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
Q = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
DP = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
DQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
InverseQ = ReadNormalizedInteger(rsaPrivateKey, halfModulusLen),
};
rsaPrivateKey.ThrowIfNotEmpty();
RSA rsa = RSA.Create();
rsa.ImportParameters(rsaParameters);
return rsa;
}
private static byte[] ReadNormalizedInteger(AsnReader reader, int length)
{
ReadOnlyMemory<byte> memory = reader.ReadIntegerBytes();
ReadOnlySpan<byte> span = memory.Span;
if (span[0] == 0)
{
span = span.Slice(1);
}
byte[] buf = new byte[length];
int skipSize = length - span.Length;
span.CopyTo(buf.AsSpan(skipSize));
return buf;
}
The Bouncycastle C# library has some helper classes that can make this relatively easy. It is not well documented unfortunately. Here is an example:
using System;
using System.IO;
using System.Security.Cryptography;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
namespace ExportToStandardFormats
{
class MainClass
{
public static void Main(string[] args)
{
var rsa = new RSACryptoServiceProvider(2048);
var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
var writer = new StringWriter();
var pemWriter = new PemWriter(writer);
pemWriter.WriteObject(rsaKeyPair.Public);
pemWriter.WriteObject(rsaKeyPair.Private);
Console.WriteLine(writer);
}
}
}
I wanted to extract public and private key as char array, and not as string. I found one solution which is a modification of answers provided by James and Vikram above. It could be helpful for someone looking for it.
public static void GenerateKeyPair()
{
char[] private_key= null;
char[] public_key=null;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
//PrivateKey
MemoryStream memoryStream = new MemoryStream();
TextWriter streamWriter = new StreamWriter(memoryStream);
PemWriter pemWriter = new PemWriter(streamWriter);
pemWriter.WriteObject(rsaKeyPair.Private);
streamWriter.Flush();
byte[] bytearray = memoryStream.GetBuffer();
private_key = Encoding.ASCII.GetChars(bytearray);
//PublicKey
memoryStream = new MemoryStream();
streamWriter = new StreamWriter(memoryStream);
pemWriter = new PemWriter(streamWriter);
pemWriter.WriteObject(rsaKeyPair.Public);
streamWriter.Flush();
bytearray = memoryStream.GetBuffer();
public_key = Encoding.ASCII.GetChars(bytearray);
}
Adding on to the above answer, to get public and private key separately in string format, you can use the following code snippet.
public static void GenerateKeyPair()
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
//Getting publickey
TextWriter textWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(textWriter);
pemWriter.WriteObject(rsaKeyPair.Public);
publicKey = textWriter.ToString();
//Getting privatekey
textWriter = new StringWriter();
pemWriter = new PemWriter(textWriter);
pemWriter.WriteObject(rsaKeyPair.Private);
privateKey = textWriter.ToString();
Console.WriteLine("public key, {0}", publicKey);
Console.WriteLine("private key, {0}", privateKey);
}
catch (Exception e)
{
Console.WriteLine($"GenerateKeyPair Failed with {e}");
Console.WriteLine(e);
}
}
I've found a blind signature implementation using Bouncy Castle in Java and I've implemented it in my C# project. But I need to un-blind the message after getting the other side's signature.
Here's my code:
class Program
{
public static AsymmetricCipherKeyPair generateKeys(int keySize)
{
RsaKeyPairGenerator r = new RsaKeyPairGenerator();
r.Init(new RsaKeyGenerationParameters(new BigInteger("10001", 16), new SecureRandom(),
keySize, 80));
AsymmetricCipherKeyPair keys = r.GenerateKeyPair();
return keys;
}
public static BigInteger generateBlindingFactor(ICipherParameters pubKey)
{
RsaBlindingFactorGenerator gen = new RsaBlindingFactorGenerator();
gen.Init(pubKey);
return gen.GenerateBlindingFactor();
}
public static byte[] blind(ICipherParameters key, BigInteger factor, byte[] msg) {
RsaBlindingEngine eng = new RsaBlindingEngine();
RsaBlindingParameters param = new RsaBlindingParameters((RsaKeyParameters) key, factor);
PssSigner blindSigner = new PssSigner(eng, new Sha1Digest(), 15);
blindSigner.Init(true, param);
blindSigner.BlockUpdate(msg, 0, msg.Length);
byte[] blinded = null;
try {
blinded = blindSigner.GenerateSignature();
} catch (Exception ex) {
Console.WriteLine(" ");
}
return blinded;
}
public static byte[] unblind(ICipherParameters key, BigInteger factor, byte[] msg) {
RsaBlindingEngine eng = new RsaBlindingEngine();
RsaBlindingParameters param = new RsaBlindingParameters((RsaKeyParameters) key,factor);
eng.Init(false, param);
return eng.ProcessBlock(msg, 0, msg.Length);
}
public static byte[] sign(ICipherParameters key, byte[] toSign)
{
Sha1Digest dig = new Sha1Digest();
RsaEngine eng = new RsaEngine();
PssSigner signer = new PssSigner(eng, dig, 15);
signer.Init(true, key);
signer.BlockUpdate(toSign, 0, toSign.Length);
byte[] sig = null;
try
{
sig = signer.GenerateSignature();
}
catch (Exception ex)
{
Console.WriteLine(" ");
}
return sig;
}
public static bool verify(ICipherParameters key, byte[] msg, byte[] sig)
{
PssSigner signer = new PssSigner(new RsaEngine(), new Sha1Digest(), 15);
signer.Init(false, key);
signer.BlockUpdate(msg, 0, msg.Length);
return signer.VerifySignature(sig);
}
public static byte[] signBlinded(ICipherParameters key, byte[] msg)
{
RsaEngine signer = new RsaEngine();
signer.Init(true, key);
return signer.ProcessBlock(msg, 0, msg.Length);
}
static void Main(string[] args)
{
AsymmetricCipherKeyPair bob_keyPair = generateKeys(1024);
AsymmetricCipherKeyPair alice_keyPair = generateKeys(1024);
try {
byte[] msg = Encoding.ASCII.GetBytes("OK");
//----------- Bob: Generating blinding factor based on Alice's public key -----------//
BigInteger blindingFactor = generateBlindingFactor(alice_keyPair.Public);
//----------------- Bob: Blinding message with Alice's public key -----------------//
byte[] blinded_msg =
blind(alice_keyPair.Public, blindingFactor, msg);
byte[] unblinded_msg =
unblind(alice_keyPair.Private, blindingFactor, blinded_msg);
//------------- Bob: Signing blinded message with Bob's private key -------------//
byte[] sig = sign(bob_keyPair.Private, blinded_msg);
//------------- Alice: Verifying Bob's signature -------------//
if (verify(bob_keyPair.Public, blinded_msg, sig)) {
//---------- Alice: Signing blinded message with Alice's private key ----------//
byte[] sigByAlice =
signBlinded(alice_keyPair.Private, blinded_msg);
//------------------- Bob: Unblinding Alice's signature -------------------//
byte[] unblindedSigByAlice =
unblind(alice_keyPair.Public, blindingFactor, sigByAlice);
//---------------- Bob: Verifying Alice's unblinded signature ----------------//
Console.WriteLine(verify(alice_keyPair.Public, msg,
unblindedSigByAlice));
// Now Bob has Alice's signature for the original message
//Console.WriteLine(Encoding.ASCII.GetString(unblindedSigByAlice));
}
Console.WriteLine(Encoding.ASCII.GetString(unblinded_msg));
} catch (Exception e) {
}
Console.ReadLine();
}
}
How can I un-blind this message?