How to load PuTTYgen keys in Bouncy castle - c#

I'm playing around with encrypting. I have got stucked on loading the keys with BouncyCastle. The keys have been generated by PuTTYgen (SSH-2 RSA 4096 bit). Is there maybe some other way to load it?
PuTTY-User-Key-File-2: ssh-rsa
Encryption: aes256-cbc
Comment: rsa-key-20160508
Public-Lines: 12
I get:
An unhandled exception of type 'System.IO.IOException' occurred in BouncyCastle.Crypto.dll
Additional information: unexpected end-of-contents marker*"
const string pubkey = #"AAAAB3NzaC1yc2EAAAABJQAAAgEAlX+AsmDF7FoR7D2jWYJTKLD6ZH6FAY9v2vPa
5+ICSQCydphiUbiJwsb3sl4yJIB6uOw4fM+22F0U/P1pxdGqUabqixNOC5X9xBzI
KbMt2xcYof/JCaNskHE2teguxdCeLlQ30QIxTRD76y6TqISS+anTtpMiGFs/LNOX
yDp7H3S5OuvRjoTRLoocEwIFNbpnkq8D+RVSdV1kkqVSMwzwRgBRRCkBfZAYW9VY
qlWNwbLCiGw3qi5mu2qorxqqF0tO2l0gLFNYdbnf9+bKavqiV7wMn3mVbcFtRhe/
29klqxos6V8Of71Q7IShFz9IXq5KdRdJoGyNJrNXKoF87UCPppa4lmOlfY40E/c5
w/LMjf63c/xx8ji7v6hY0sHt7XdWUB3V1XBPScypk7PxNS13/RtdzjeOn/8W9tcV
tqX6b7XFU/AwIrkBDa47JGnuOJnmzmMzHDPKeZwaEQT4BZRzUa6yKq3EZCLymkBB
W9lRQkKY3qg8OJX+FhAQ7c5pCOl3PFmFjuDMbRWTaF/nJj/qPfvND+w7a86fxUUC
HPokcYGLVZUBWTfnx3q/StbjaQBQDPMxogLh/yCZywHFqO0xGaA8SQAM2Vyd7r7l
waPQhFIJhVrXuaz0Ok+TE5jqe5yTB0gZ8pcVDq/GWRXBAUwXu50UeZBurcGHEXk5
HhFdFlk=";
const string prikey = #"...";
var publickey = Convert.FromBase64String(pubkey);
//var privatekey = Convert.FromBase64String(prikey);
Org.BouncyCastle.Crypto.AsymmetricKeyParameter publicAsymmetricKey = Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(publickey);
//Org.BouncyCastle.Crypto.AsymmetricKeyParameter privateAsymmetricKey = Org.BouncyCastle.Security.PrivateKeyFactory.CreateKey(privatekey);
ADDED:
I generated a new key with "ssh-keygen -t rsa -b 4096" and tryed to load it with pemreader:
const string pubkey = #"AAAAB3NzaC1yc2EAAAADAQABAAACAQDGzR88DIxKRUvEJDAa0zcRq0O39M5i9vwkb9r+mPi56eYPEZJfMcZZmrtMoBoCJM3g0CmdHHktanRnjwXI+Gw6zRIbe3Lnz8rq+IyCDQnJQzHMlFJkwt7tuFzLWwNoWL5bWN0+Y6h8pB6KudPxsutQfNVTAyI00JhhhMk1T0FSF82BHP+HKI+lsGrTaBhDHnbZryk5Bf00sAJLVvS5kE7LOthVAWHGKZbV/PJnDcXQN8Hxto9mWy0iELTsUYzwf1VfIvB1Ugyv/rs3Vn5A98p2BaXidwYrgc3NhKliESQyl9KYggF3ody99zlMzWQfdmpfJEBzjxjNIMaD/NFBgTLHSbEzyXDVLcvCocmNaJeEQ4uGcqqw2Oe7d9RDZikllttLCtPQSQ49N/1JI5CRHEn9bz7RNX4aZWPbzw1l5y6fcsTXcPsVHb+bGmhcobSbWZvAnLMrHA0FI+47yNrBlp+qXBXfxO3/sSK3yU1lJEUosmfb3nPvvt0bt0TlwLYC12gMLk1Z0ZAoHxR7qw7la/HoZXhQqqi57RdDWbHiX6zxFPDVMd48fc8m9Wnwv2+AIcBRsErnL4YWT/4Pb57+ehg+HbKy5cCgo6wsHYB9IjoXIfNQAz2dAbXHr2C4zo0BKlabZSqv+ZoTBP5NwSZItMeRv1eCreg7ejJ5HR8NIJaAZQ==";
var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(pubkey));
// KeyParameter is still null
var KeyParameter = (Org.BouncyCastle.Crypto.AsymmetricKeyParameter)pemReader.ReadObject();
ADDED:
I was able to generate a public key and also load a unencrypted private key
(https://rietta.com/blog/2012/01/27/openssl-generating-rsa-key-from-command/)
openssl genrsa -des3 -out private.pem 4096
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
openssl rsa -in private.pem -out private_unencrypted.pem -outform PEM
var publickey = Convert.FromBase64String(pubkey);
var privatekey = Convert.FromBase64String(prikey);
var asnprivate = Org.BouncyCastle.Asn1.Asn1Object.FromStream(new MemoryStream(privatekey));
var privStruct = new Org.BouncyCastle.Asn1.Pkcs.RsaPrivateKeyStructure((Org.BouncyCastle.Asn1.Asn1Sequence)asnprivate);
AsymmetricKeyParameter publicAsymmetricKey = PublicKeyFactory.CreateKey(publickey);
But I was not able to load the encrypted private key...

The PuTTY .ppk key format is proprietary to PuTTY.
I'm pretty sure there's no support for .ppk files in Bouncy castle.
You need to convert your keys to OpenSSH format. For that see How to convert SSH keypairs generated using PuttyGen(Windows) into key-pairs used by ssh-agent and KeyChain(Linux).

With the openssl PEM I was able to make it to work. I continue to find out how to load load the protected private key.
Thanks for all the input.
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1;
namespace EncryptTest1
{
class Program
{
static void Main(string[] args)
{
const string pubkey = #"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuQhcZKNnIC2bvJhI5Yzq
oH9uGAHuiAt9bOXdIrsaeEwsIUMuyNfXkLozwlcJb0YLgqhpP3gNLb58WCb1AvkV
ZHgzLQbay93HwmBiEPW6LZIKGsHwh1awBmJpeWhU8h3oNP+KpNq44H+jAeM9G9En
W0y7+AUbe3pnisOWVZHmM72A2mLq1piCOjmvjegzgB8/rU7ZkRrNadeFO7DBP0Ew
6eElS3iXfFHJmbK+L5lL0pFxvUMRfGVC33NQkuSoWiQJZsYyUgIk6WSOQm3t5fw6
VESMTfd1xKlaBGp7IrWksthEF+XAu5ziAcSdjBwJLEFzJ2830cQ4GhFS3JtU/Iao
6MbJkUuaHoI3Xx9C7d6RiKbK1uZrOHjyVsUhqpYwyAUdrViraj0pybxbE2GwbRC4
/NyHuPKG3gBULiGYbl8wwlhl71nRVdkPQtGw36kmGWHRk3ERM6JgLWsDZjED4ak2
Na6VzIvnqVs0HROl/4Rd4rHZvl9t39tfVBtofEkD4C0B5BdgW/B2XQM7Fd4celn3
q/+FKmvaVydoFGG0s4YZL6hsqOISfhdT10jGrwNKe+zupnrXZO66mtX8rvPtY4x2
WXcHrbp1cgSr+2r8Mteh8Eff+6rqArU1i1ezDm3SrKs1WMFKu1hRKDYudfOaB/L7
gvEvD+lUE9qnjlYUBGSXKMUCAwEAAQ==";
const string prikey = #"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";
var publickey = Convert.FromBase64String(pubkey);
var privatekey = Convert.FromBase64String(prikey);
var asnprivate = Asn1Object.FromStream(new MemoryStream(privatekey));
var privStruct = new RsaPrivateKeyStructure((Asn1Sequence)asnprivate);
RsaKeyParameters privateAsymmetricKey = new RsaKeyParameters(true, privStruct.Modulus, privStruct.PrivateExponent);
RsaKeyParameters publicAsymmetricKey = (RsaKeyParameters)PublicKeyFactory.CreateKey(publickey);
var inputBytes = Encoding.UTF8.GetBytes("the message");
Console.WriteLine("--- Message: ----");
Console.WriteLine(Encoding.UTF8.GetString(inputBytes));
IAsymmetricBlockCipher cipher = new RsaEngine();
cipher.Init(true, publicAsymmetricKey);
var cipheredBytes = cipher.ProcessBlock(inputBytes, 0, inputBytes.Length);
Console.WriteLine("--- Enc utf8: ----");
Console.WriteLine(Encoding.UTF8.GetString(cipheredBytes));
Console.WriteLine("--- Enc Base64: ----");
Console.WriteLine(Convert.ToBase64String(cipheredBytes));
cipher.Init(false, privateAsymmetricKey);
var deciphered = cipher.ProcessBlock(cipheredBytes, 0, cipheredBytes.Length);
Console.WriteLine("--- Dec: ----");
Console.WriteLine(Encoding.UTF8.GetString(deciphered));
Console.ReadLine();
}
}
}

Related

C# - How to Decrypt an Encrypted Private Key with Bouncy Castle

I have a private key that was generated by running:
openssl req -new -sha384 -x509 -days 63524 -subj "/C=CA/ST=State/L=City/O=Org/CN=Org-CA" -extensions v3_ca -keyout Org-CA.key -out Org-CA.pem -passout pass:pass1234
I need to use this private key and certificate to sign client CSRs. I used to use OpenSSL to do this, but I need to do this in C# instead.
I've found examples of what I want to do online, but those examples are using bouncy castle and an un-encrypted private key.
The key file looks like this:
-----BEGIN ENCRYPTED PRIVATE KEY-----
....
-----END ENCRYPTED PRIVATE KEY-----
And I'm trying to load it by doing this:
var privatekey = File.ReadAllBytes(#"Org-CA.key");
var rsaKeyParameters = PrivateKeyFactory.DecryptKey("pass1234".ToArray(), privatekey);
But thats not working. I get an error saying Wrong number of elements in sequence (Parameter 'seq')'
Is this how I'm supposed to be decrypting and loading the private key?
Depending on your .NET version, you may not need BouncyCastle at all. As of .NET Core 3.1 there is RSA.ImportEncryptedPkcs8PrivateKey() for DER encoded encrypted private PKCS#8 keys and as of .NET 5.0 there is even RSA.ImportFromEncryptedPem() for PEM encoded encrypted keys.
Otherwise with C#/BouncyCastle the import of an encrypted private PKCS#8 key is available e.g. with:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
...
string encPkcs8 = #"-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----";
StringReader stringReader = new StringReader(encPkcs8);
PemReader pemReader = new PemReader(stringReader, new PasswordFinder("<your password>"));
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
...
with the following implementation of the IPasswordFinder interface:
private class PasswordFinder : IPasswordFinder
{
private string password;
public PasswordFinder(string pwd) => password = pwd;
public char[] GetPassword() => password.ToCharArray();
}
If necessary, a conversion from keyParams to System.Security.Cryptography.RSAParameters is possible with:
using Org.BouncyCastle.Security;
...
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(keyParams);
which can be imported directly e.g. with RSA.ImportParameters().
Edit:
After digging through the C#/BouncyCastle source code, your way is also possible (which I didn't know before).
DecryptKey() has several overloads, one of which can handle the DER encoded encrypted key as already suspected by Maarten Bodewes in his comment. The latter can be easily generated with the Org.BouncyCastle.Utilities.IO.Pem.PemReader() class.
Note that this PemReader is different from the one in the first implementation (different namespaces), which is why I use the namespace explicitly in the following code snippet. With this approach, the RsaPrivateCrtKeyParameters instance can be generated as follows:
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
...
StringReader stringReader = new StringReader(encPkcs8);
Org.BouncyCastle.Utilities.IO.Pem.PemReader pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pem = pemReader.ReadPemObject();
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.DecryptKey("<your password>".ToCharArray(), pem.Content);
...
pem.Content contains the DER encoded encrypted private PKCS#8 key.

Failed to import RSA private key: "Bad Version of provider"

I have a random private key ("C:\tmp\private.key"):
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDHikastc8+I81zCg/qWW8dMr8mqvXQ3qbPAmu0RjxoZVI47tvs
kYlFAXOf0sPrhO2nUuooJngnHV0639iTTEYG1vckNaW2R6U5QTdQ5Rq5u+uV3pMk
7w7Vs4n3urQ6jnqt2rTXbC1DNa/PFeAZatbf7ffBBy0IGO0zc128IshYcwIDAQAB
AoGBALTNl2JxTvq4SDW/3VH0fZkQXWH1MM10oeMbB2qO5beWb11FGaOO77nGKfWc
bYgfp5Ogrql4yhBvLAXnxH8bcqqwORtFhlyV68U1y4R+8WxDNh0aevxH8hRS/1X5
031DJm1JlU0E+vStiktN0tC3ebH5hE+1OxbIHSZ+WOWLYX7JAkEA5uigRgKp8ScG
auUijvdOLZIhHWq7y5Wz+nOHUuDw8P7wOTKU34QJAoWEe771p9Pf/GTA/kr0BQnP
QvWUDxGzJwJBAN05C6krwPeryFKrKtjOGJIniIoY72wRnoNcdEEs3HDRhf48YWFo
riRbZylzzzNFy/gmzT6XJQTfktGqq+FZD9UCQGIJaGrxHJgfmpDuAhMzGsUsYtTr
iRox0D1Iqa7dhE693t5aBG010OF6MLqdZA1CXrn5SRtuVVaCSLZEL/2J5UcCQQDA
d3MXucNnN4NPuS/L9HMYJWD7lPoosaORcgyK77bSSNgk+u9WSjbH1uYIAIPSffUZ
bti+jc1dUg5wb+aeZlgJAkEAurrpmpqj5vg087ZngKfFGR5rozDiTsK5DceTV97K
a3Y+Nzl+XWTxDBWk4YPh2ZlKv402hZEfWBYxUDn5ZkH/bw==
-----END RSA PRIVATE KEY-----
I tried to use the RSACryptoServiceProvider.ImportCspBlob to import it but it failed with the error:
System.Security.Cryptography.CryptographicException: Bad Version of
provider.
Full stack trace:
Failed: System.Security.Cryptography.CryptographicException: Bad Version of provider.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils._ImportCspBlob(Byte[] keyBlob, SafeProvHandle hProv, CspProviderFlags flags, SafeKeyHandle& hKey)
at System.Security.Cryptography.Utils.ImportCspBlobHelper(CspAlgorithmType keyType, Byte[] keyBlob, Boolean publicOnly, CspParameters& parameters, Boolean randomKeyContainer, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.ImportCspBlob(Byte[] keyBlob)
at ConsoleApplication3.Program.DecodeRSA(Byte[] data, Int32 c_data) in C:\Users\myuser\Documents\Visual Studio 2015\Projects\myproj\ConsoleApplication3\Program.cs:line 28
at ConsoleApplication3.Program.Main(String[] args) in C:\Users\myuser\Documents\Visual Studio 2015\Projects\myproj\ConsoleApplication3\Program.cs:line 14
Press any key to continue . . .
Any idea what I am doing wrong?
This is my code:
using System;
using System.Security.Cryptography;
namespace ConsoleApplication3
{
class Program
{
static public byte[] privateKey;
static void Main(string[] args)
{
try
{
privateKey = System.IO.File.ReadAllBytes(#"C:\tmp\private.key");
DecodeRSA(privateKey);
}
catch(Exception e)
{
Console.WriteLine("Failed: {0}", e);
}
}
static public void DecodeRSA(byte[] data)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportCspBlob(Program.privateKey);
}
}
}
}
Your private key has a PKCS1-PEM-Format. Private key BLOBs have another format. As already mentioned in the comments, the formats are very different and cannot be easily converted (see e.g. here). Of course you can use a PKCS1-PEM-key, but it is not that easy. Here are some options:
Possibility 1:
If you use .NET Core 3.0 there is a direct support for reading a PKCS1-key (see also here):
byte[] privateKeyPkcs1PEM = File.ReadAllBytes(#"C:\tmp\private.key"); // PKCS1 - PEM
byte[] privateKeyPkcs1DER = ConvertPKCS1PemToDer(Encoding.UTF8.GetString(privateKeyPkcs1PEM));
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(privateKeyPkcs1DER, out _);
// use e.g. rsa.Decrypt(...)
However, the ImportRSAPrivateKey-method can only process the DER-format which is essentially the binary format of the PEM-format (for more details see here or here). Thus, you have to convert the PEM-format into the DER-format with something like
private static byte[] ConvertPKCS1PemToDer(string pemContents)
{
return Convert.FromBase64String(pemContents
.TrimStart("-----BEGIN RSA PRIVATE KEY-----".ToCharArray())
.TrimEnd("-----END RSA PRIVATE KEY-----".ToCharArray())
.Replace("\r\n",""));
}
Alternatively, OpenSSL can also be used for the conversion:
openssl rsa -inform PEM -outform DER -in C:\tmp\private.key -out C:\tmp\private.der
Possibility 2:
You can convert your PKCS1-PEM-key into an PKCS8-DER-key using OpenSSL. The appropriate command is:
openssl pkcs8 -topk8 -inform pem -in C:\tmp\private.key -outform der -nocrypt -out C:\tmp\privatepkcs8.der
The difference between PKCS1- and PKCS8-format is explained here. Then you can import the key with built-in .NET-methods (see also here, section PKCS#8 PrivateKeyInfo):
byte[] privateKeyPkcs8DER = File.ReadAllBytes(#"C:\tmp\privatepkcs8.der"); // PKCS8 - DER
CngKey cngKey = CngKey.Import(privateKeyPkcs8DER, CngKeyBlobFormat.Pkcs8PrivateBlob);
RSA rsa = new RSACng(cngKey);
// use e.g. rsa.Decrypt(...)
Possibility 3:
If third-party-libraries may be used, BouncyCastle is also a possibility:
StreamReader reader = File.OpenText(#"C:\tmp\private.key"); // PKCS1 - PEM
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
Pkcs1Encoding pkcs1Encoding = new Pkcs1Encoding(new RsaEngine());
// E.g. decryption
pkcs1Encoding.Init(false, keyPair.Private);
byte[] decrypted = pkcs1Encoding.ProcessBlock(encrypted, 0, encrypted.Length);
Possibility 4:
Another possibility is the use of the DecodeRSAPrivateKey-method from JavaScience which can process a PKCS1-key. However, the DecodeRSAPrivateKey-method can only process the DER-format. Thus, you must first manually convert the PEM-format into the DER-format using e.g. ConvertPKCS1PemToDer or OpenSSL.
Thanks to the #Topaco answer, I understood that I need to find the CSP blob and like he mentioned, it is undocumented.
So I just used byte[] a = rsa.ExportCspBlob(true); to get it.
I converted the bytes to base64 and this is a correct key format in base64 that worked for me:
BwIAAACkAABSU0EyAAQAAAEAAQD99dvdVctFcYP6fGCvz/8QcoJqjpfKMPxCIsVRAZSCaKTB6Dl0DbEQBcaLNe8Cm31lzMYyf/2vh6gM+GUHmKcBYo2Z7JvauTGXFXEyv02ai8RINlvAGAicZwWoyGJb5h4sM881Q5+BuDTcoyefk+b7x7KBQjMD/wNuPCWijZ0lsP+Gt1tPryE757QDDl95jQk04ZS+70vGOAO836f+RCyeA6c0ZEA1eYzsM/PVsv+nLwh7pTj7KLFSha5CM304SdcDnyOnt1ARyv1BQsRhyN3IAOH/Se00OfWhcc0sZCjg+xvDebKuoODHDhUfHJPchOmyvhSxjyNACJuxg1uGh3XRmaPoceXXFCuNhFGheK5cQrfUGHpWeJKrpWM/+f3XcrYob0jQCloBIicXfvhhPnkPojiOquxmjy0rA8/JRjHov3+znJY+pQgFC5cUmvGWxhWygm+qDwYco6yCSRkkaIp/K39uJXQ2pQf9XapqjtAJipRo5xX0o/itiDyF1qPT7TumZROMUhU3znXGnxPelZ2bA7SgPiu6BBKADfqG1XJE1K50ydaEfyXYceYHIs7UAMLw9aTptqHbPPGp1hDL2xpWBR6hvqkPqouiVJ7VgPHkjxwT/hgXBvJbHOm/ghq/xA/1oTtXLJHXCASVdylt+nwauOp5qR0Dfdbz7IQGjChYzBHuqDuKorpmfHhZl+bDTHpJ1PjWrojoBfAt2v5zlBnw/ipjkD9MXKrNlPqbgeYXUAeAzfFKQhF2kr3zlmExIS8=
It is important to mentioned that I just need that this function will pass successfully because I am working on a blackbox application and I can't change the code, I need to provide it with the right input.

Decrypt message using RSA with password private key using pem file in C#

I'm trying to decode the HelloWorks callback signature to add security to an endpoint. As it stays in the documentation I need to generate a private key using the following openssl command:
openssl genrsa -des3 -out helloworks.pem 4096
and then make the public key:
openssl rsa -in helloworks.pem -outform PEM -pubout -out public.pem
I have created both keys and configured it. Now I need to decode base64 the X-Helloworks-Signature sent by them, then decrypt the result using the private key.
I have been trying several ways to do this in C# but with no luck. One approach that I have done using BouncyCastle library is:
var signature = mvcContext.HttpContext.Request.Headers["X-Helloworks-Signature"];
var url = mvcContext.HttpContext.Request.Path.Value;
var bytesToDecrypt = Convert.FromBase64String(signature);
AsymmetricCipherKeyPair keyPair;
var pemPath = Path.Combine(env.ContentRootPath, "./helloworks.pem");
using (var reader = File.OpenText(pemPath))
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader, new PasswordFinder("password")).ReadObject();
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
decryptEngine.Init(false, keyPair.Private);
var decryptedToken = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
But it always throws the same exception message:
Org.BouncyCastle.Crypto.DataLengthException: 'input too large for RSA cipher.'
I have been trying other approaches that I have found here in SO and the web but no luck.
How can I use the private key pem file to decrypt the signature in C#, specifically .Net Core 2?
Update 1
An example of the signature is:
X-Helloworks-Signature: bGNTNUVOZ0w5akx4U1VXUTFrcHQ0ZmZSZU1KQnNpbDhwVDFGVllSMzF5aVdtQkZOcTRMTFNrY2FmV3o5aFZJVk5rbmVRUEdydTZLZ1BHQ1dCdkFYVVRMWUs1bUYxSXplam5mZVEwVFZvMjFZRS1rM0l4SDBkNU45clJDX1RYOVZPcThwUzI5V2pTd1k2d0kxaGQ0VXpiSkU2NERJaVljaWxCWkJwVVJhN0NRcGphbkxlZV8tZFMxQWtUbTdRUHdEVTZtVDNnc2ZPQkNiMFZMeUR2TGVtdmVTZldXZnVUOXg0NTRYSU1rZjE3elRvR0xHT29YeEpKWjk5LUcyc3VjWkk5NWpmRldvaGo0S2Z0dXV1SHMtMmpZajZuNXhpMFI5S0hmT2xNRkRwRlFtbEpUbG9TTURGREVPeDE3RjNoX1NXMk1RSUR2b2hGazBtTkstMk9OVHhHQ05tQXhuS1c5Nm9SN0N4QlJnQ2xyLXlvOXJTWXp2anNlVVhGZ0hqakYzZE8ybGhUdXFPV1A0NU9TaUVoY21zSHVMRnV0Zy10a0J5QTJLYTB2Yk1sNi1wMVlLR09QZVdMVm9QN0k5ellyWFdNRFl0S2dPaXZkSldEa1B0SmdvbVMwbU1DdWMyblBFUHFxUTEtQkdJMHNFdjNNWVFfYVNjeGJsZ2p2VHVZZzRmZy1VXzk3R0pON1ZsM1IwWllZSF9QU2syQll6LTN3Vjc3QjMxbGFjT3lWSU15WDVJdktKWXIwTXZMQ190cjIwZk9hZkx5c011eWpsSXV6Tl96Q2VHbkpfbkJiQnJYNXNyNmktZDB4UW9rc0JKSUtUZUNMR3dVWWEtVEJVUE5FeWplM09RT0NOYW02R242OEdLeDRZdTlzZDVNa1BCQ1B6NTI3aUhoYm9aLTg9
Thanks to James K Polk comment I ended doing the following method:
private static byte[] Base64Decode(StringValues signature)
{
string incoming = signature.ToString().Replace('_', '/').Replace('-', '+');
switch (signature.ToString().Length % 4)
{
case 2:
incoming += "==";
break;
case 3:
incoming += "=";
break;
}
return Convert.FromBase64String(incoming);
}
and modifying my code from:
var bytesToDecrypt = Convert.FromBase64String(signature);
to:
var decoded = Base64Decode(signature);
var text = Encoding.ASCII.GetString(decoded);
var bytesToDecrypt = Base64Decode(text);
I needed to decode two times the signature using the URL-safe version of base64 decoding.

RSACryptoServiceProvider in Node js

I have to sign a string in node js, in the same way of a C# application, which uses RSACryptoServiceProvider. In fact it uses
X509Certificate2 certificate = new X509Certificate2("file.pfx", "aPassword",
X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSACryptoServiceProvider.Create();
rsa.FromXmlString(certificate.PrivateKey.ToXmlString(true));
signer = new RSAPKCS1SignatureFormatter(rsa);
signer.SetHashAlgorithm("SHA256");
byte[] signedData = signer.CreateSignature("a string");
string signedString = Convert.ToBase64String(signedData);
I've tried to do the same in node js, so far, using jsonwebtoken in this way
var kdrPrivateKey = fs.readFileSync('private_key.pem');
var authorizationSigned = jwt.sign("a string", kdrPrivateKey, { algorithm: 'RS256'});
var authorizationBase64 = Base64.encode(authorizationSigned);
I also got the private_key from file.pfx in this way
openssl pkcs12 -in file.pfx -nocerts -out private_key.pem -nodes
My question, finally is, is the node js code equivalent to the C# code? If not, how can I make the C# code in a node js way?
Thanks!
Use RSA XML Decrypter for Node. Decrypt a string by private RSA XML C# key in Node.js.
var RSAXML = require('rsa-xml'),
// base64 encoded private rsa xml key
var privateKey1 = 'PFJTQUtleVZhbHVlPjxNb2R1bHVzPjUxbCtyaHRzRmQvQ3NOb0U5VW9kdWorS0Vqd0F2YWZUZmI1N3Zldit3b3ZRbjdoVURrdzlCbVVMOTdSSC9zaC9udVN2Qkl3RGRlVVZTZzJDaXo4bE5McmY0WTVlMmI1NUtNZVBzR3lIV29abXhpbkdQUzd1cjRLSkhPZmVCYStHeGRDOC80cFdCSjZFK3BCajNkQ2JQRFBLWVZ6N0RRTUhkWGNRWjRCcTR2OD08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjxQPitRdkdsTE14anZMaHJFZi9ab011VklBR05xMXh6YUptcGZCa2E0dDNsY0RZR2xoUVI1OXZzWWFORGwzVTQzaVVZZ3JYa0NtbFVnRXB5QXBUS05hL3RRPT08L1A+PFE+N2M4NDNFZXRrM0pqeEFRRDFKUGg0QzFONkNyeCtkWDVjSXk1Z2xkY2Q3ODlYenJFU2dTUDhEWDd5U2puT2VXZmxEdmlyR0h6YVNXdmZWaTNKNXZBWXc9PTwvUT48RFA+T1JEYXN1NFFtQW5OYmpXZEx6YzE0WVRvWjVUOHM3clh2SVJGN21LcHh6WEdEdHRYb2VIRnJTOEFtVjhremU2dVNYemtnaE1ZMzU2R25XRElSMTVWMVE9PTwvRFA+PERRPkh5VC9kbUh3eXBtMWxTdE5jUjY0KzBvVHBPOVM1M3h0Z1o3OGdLUitXTFIwRGkrOUcxQ0RwVnI4a2JqSXA1MTZDOGpZQSttRUhtWXdHSU53NFVBVnJ3PT08L0RRPjxJbnZlcnNlUT5SVXowVDA4eDZSaHg4U2RMQ2dQVXNNenJKb29qQjZDTmR2MkpOcHNaN2Nnc1k1MDhEQjAwd29kQlFrem90SGJ0QVhTa1VsN2d0QXI0TGlFejVORU56Zz09PC9JbnZlcnNlUT48RD5JVmVPRmluMTZyUjIwREIrVjNCVGxzODlKeGRHWkxtbWF0c1prQXZPTkhGSERoc3RqaFAzRlpBRVBnZXUrcGdnZ0hZUDNVQVA2RWdDODBzUzB6TzB1T2h0UGIzNDllOSs2WnhlMjJhaWV0WTFabFlQT201di9YbE5HZlhOZWQvbjhUYUJZRHB3YnZTVUw0T2M1eFJOeWFnU2x4Mi9GN1h3NHBkQmw0cG9LZ1U9PC9EPjwvUlNBS2V5VmFsdWU+';
var encBase64_1 = '4TkhLlVNK27LQXSRIftGCbwOI2qnBGD0eR61g53KAdw5J+BTpczLpdWGn/9v3I6xGPQYvTB1F2cmbcuBboNGa18b+8gigwbat9vTEuLjD/OKl2V+jpqKf3xxwYYuz1s05HyV8KWxbS01M/iwjWPrcNRWh3vWff9pRAu8Z6KQAQc=';
var privateKey2 = 'PFJTQUtleVZhbHVlPjxNb2R1bHVzPjUxbCtyaHRzRmQvQ3NOb0U5VW9kdWorS0Vqd0F2YWZUZmI1N3Zldit3b3ZRbjdoVURrdzlCbVVMOTdSSC9zaC9udVN2Qkl3RGRlVVZTZzJDaXo4bE5McmY0WTVlMmI1NUtNZVBzR3lIV29abXhpbkdQUzd1cjRLSkhPZmVCYStHeGRDOC80cFdCSjZFK3BCajNkQ2JQRFBLWVZ6N0RRTUhkWGNRWjRCcTR2OD08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjxQPitRdkdsTE14anZMaHJFZi9ab011VklBR05xMXh6YUptcGZCa2E0dDNsY0RZR2xoUVI1OXZzWWFORGwzVTQzaVVZZ3JYa0NtbFVnRXB5QXBUS05hL3RRPT08L1A+PFE+N2M4NDNFZXRrM0pqeEFRRDFKUGg0QzFONkNyeCtkWDVjSXk1Z2xkY2Q3ODlYenJFU2dTUDhEWDd5U2puT2VXZmxEdmlyR0h6YVNXdmZWaTNKNXZBWXc9PTwvUT48RFA+T1JEYXN1NFFtQW5OYmpXZEx6YzE0WVRvWjVUOHM3clh2SVJGN21LcHh6WEdEdHRYb2VIRnJTOEFtVjhremU2dVNYemtnaE1ZMzU2R25XRElSMTVWMVE9PTwvRFA+PERRPkh5VC9kbUh3eXBtMWxTdE5jUjY0KzBvVHBPOVM1M3h0Z1o3OGdLUitXTFIwRGkrOUcxQ0RwVnI4a2JqSXA1MTZDOGpZQSttRUhtWXdHSU53NFVBVnJ3PT08L0RRPjxJbnZlcnNlUT5SVXowVDA4eDZSaHg4U2RMQ2dQVXNNenJKb29qQjZDTmR2MkpOcHNaN2Nnc1k1MDhEQjAwd29kQlFrem90SGJ0QVhTa1VsN2d0QXI0TGlFejVORU56Zz09PC9JbnZlcnNlUT48RD5JVmVPRmluMTZyUjIwREIrVjNCVGxzODlKeGRHWkxtbWF0c1prQXZPTkhGSERoc3RqaFAzRlpBRVBnZXUrcGdnZ0hZUDNVQVA2RWdDODBzUzB6TzB1T2h0UGIzNDllOSs2WnhlMjJhaWV0WTFabFlQT201di9YbE5HZlhOZWQvbjhUYUJZRHB3YnZTVUw0T2M1eFJOeWFnU2x4Mi9GN1h3NHBkQmw0cG9LZ1U9PC9EPjwvUlNBS2V5VmFsdWU+';
var encBase64_2 = '4TkhLlVNK27LQXSRIftGCbwOI2qnBGD0eR61g53KAdw5J+BTpczLpdWGn/9v3I6xGPQYvTB1F2cmbcuBboNGa18b+8gigwbat9vTEuLjD/OKl2V+jpqKf3xxwYYuz1s05HyV8KWxbS01M/iwjWPrcNRWh3vWff9pRAu8Z6KQAQc=';
var rsa = new RSAXML();
// a private key
rsa.importKey(privateKey1, 'pKey1');
var decrypted1 = rsa.decrypt(encBase64_1, 'pKey1');
console.log('decrypted:', decrypted1);
// another private key
rsa.importKey(privateKey2, 'pKey2');
var decrypted2 = rsa.decrypt(encBase64_2, 'pKey2');
console.log('decrypted:', decrypted2);

Generate a CMS PKCS#7 file with BouncyCastle in C#

I'm starting to cryptography.
I need to generate a PKCS # 7 file with XML, an RSA private key (which is not included in the certificate is a file extension .key) and a certificate .cer extension.
For this i'm using a BouncyCastle.
Edit:
Thanks #khlr for your help but I can not solve my problem. When sending the data to the AC returns me "Invalid CMS".
I have this code:
public static byte[] FirmaBytesMensaje(byte[] argBytesMsg, X509Certificate2 argCertFirmante)
{
try
{
//Add message in object ContentInfo
ContentInfo infoContenido = new ContentInfo(argBytesMsg);
SignedCms cmsFirmado = new SignedCms(infoContenido);
CmsSigner cmsFirmante = new CmsSigner(argCertFirmante);
cmsFirmante.IncludeOption = X509IncludeOption.EndCertOnly;
// Sign message PKCS #7
cmsFirmado.ComputeSignature(cmsFirmante);
// Encodeo el mensaje PKCS #7.
return cmsFirmado.Encode();
}
catch (Exception excepcionAlFirmar)
{
throw new Exception("***Error: " + excepcionAlFirmar.Message);
}
}
Signing on PKCS # 7 but this makes with a "PFX" certificate, that is, that contains the private key in a ".pfx" file.
When I use the OpenSSL command:
openssl smime -sign -signer cert.crt -inkey private.key -out file.xml.cms -in file.xml -outform PEM -nodetach
The AC responds well.
How I can do this with BouncyCastle and cer and key files? I am going crazy! :-(
Unfortunately there seems to be no bouncycastle API documentation for C#. Never the less there's a Java reference which is said to be quite similar to the C# API.
Hence the getEncoded()-method (look for a C# equivalent, e.g. GetEncoded()) yields a ASN.1 encoded byte[].
You could then go ahead and get a string from that (note that I'm not familiar with ASN.1 encoding. This is just a guess πŸ˜‰):
byte[] buffer = datosFirmados.GetEncoded();
string signedDataString = System.Text.Encoding.UTF8.GetString(buffer, 0, buffer.Length);
Edit:
Maybe the AsnEncodedData-class would be more appropriate for that task:
byte[] buffer = datosFirmados.GetEncoded();
var asndata = new AsnEncodedData(buffer);
const bool multiline = true;
string signedDataString = asndata.Format(multiline);
It's been a quite time, but still no answer.
You will need to merge cert and key file together as below.
using (System.Security.Cryptography.X509Certificates.X509Certificate2 _pub = this.PublicKey.X509Certificate2)
{
using (X509Certificate2 _pri = _pub.CopyWithPrivateKey(_rsa))
{
var _infoContenido = new System.Security.Cryptography.Pkcs.ContentInfo(Message);
SignedCms _signedCms = new SignedCms(_infoContenido);
CmsSigner _cmsSigner = new CmsSigner(_pri);
if (IncludeSignDate)
{
_cmsSigner.SignedAttributes.Add(new Pkcs9SigningTime(DateTime.Now)); // [2020-05-02] μ„œλͺ…ν•œ λ‚ μ§œ 속성 μΆ”κ°€
}
_cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
// Sign message PKCS #7
_signedCms.ComputeSignature(_cmsSigner);
var _signedMessage = _signedCms.Encode();
}
}

Categories