Openssl verification fails - c#

I have a zip file which I'm signing using c# code with Bouncy castle. The generated signature file is identical ( I have compared output string as well as binary code) to signature file generated with openssl using following command
openssl dgst -sha256 -sign content_private_key.pem -out content.zip.sig content.zip
But when I verify signature file (generated using c# code) using openssl it shows "Verification Failure error in dgst" error. Whereas signature file generated using openssl gets verified successfully.
Command I'm using for verification is
openssl dgst -sha256 -verify content_public_key.pem -signature content.zip.sig content.zip
openssl output
What could be the issue ?
Here is the C# code I'm using to sign the data
/// <summary>
/// Method to generate signature file
/// </summary>
private void GenerateSignatureFile(string sourceFile)
{
try
{
var bytesToSign = File.ReadAllBytes(sourceFile);
var sig = Sign(bytesToSign);
var fileContent = Encoding.GetEncoding(1252).GetString(sig);
using (var sw = File.CreateText(Path.Combine(_projectLocation, _sigFileName)))
{
sw.Write(fileContent);
}
}
catch (Exception ex)
{
LoggingService.Log(ex.Message);
var errorWhileCreatingSignatureFile = Resource.ResourceManager.GetString("ErrorWhileCreatingSignatureFile");
throw new Exception(errorWhileCreatingSignatureFile, ex);
}
}
public byte[] Sign(byte[] bytes)
{
var key = ReadPrivateKey();
/* Make the key */
var keyParameter = new RsaKeyParameters(key.IsPrivate, ((RsaPrivateCrtKeyParameters)key).Modulus, ((RsaPrivateCrtKeyParameters)key).Exponent);
/* Init alg */
ISigner sig = SignerUtilities.GetSigner("SHA256withRSA");
/* Populate key */
sig.Init(true, key);
/* Calc the signature */
sig.BlockUpdate(bytes, 0, bytes.Length);
return sig.GenerateSignature();
}
private AsymmetricKeyParameter ReadPrivateKey()
{
AsymmetricCipherKeyPair keyPair;
using (var reader = new StringReader(_privateKey))
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
return keyPair.Private;
}

[Solution in comments]
If files were identical then verification will work in both cases because digital signature verification is deterministic. It means that files are not equal.
A signature result of type byte[] is binary and not text. Any attempt to apply a text encoding in any language will produce unexpected results because it contains non-representable characters
You have an issue saving binary data to file here
var fileContent = Encoding.GetEncoding(1252).GetString(sig);
using (var sw = File.CreateText(Path.Combine(_projectLocation, _sigFileName)))
Change it with
File.WriteAllBytes(string path, byte[] bytes)

Related

iText 7 pdf signature invalid when created manually

I want to digitally sign pdf documents using iText 7. The signature is created by an external service which returns a PKCS1 signature only. I then have to create and apply the PKCS7.
There is a good documentation for this scenario from iText: https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7
Sample application
I have created a sample application which signs pdf documents via local certificate. This sample application can be cloned from https://github.com/suntsu42/PdfSignSamplePkcs1. In this sample application are two different ways of creating the PKCS7. Once manually and once via a IExternalSignature(PrivateKeySignature) implementation.
For both cases, the pdf digest which must be signed is created in the same way. The only difference is the way the PKCS7 is created.
The project on github (https://github.com/suntsu42/PdfSignSamplePkcs1) is complete and self contained. In the resources folder is a private key file (pfx) used for creating the signature as well as the root certificate. In order to run the example, it should be enough to just change the value of the resourcePath variable to accommodate your local system.
The signature creation can be toggled by changeing the value of createSignatureViaPlainPkcs1
using iText.Kernel.Pdf;
using iText.Signatures;
using System;
using System.IO;
namespace PdfSignSamplePkcs1
{
class Program
{
static void Main(string[] args)
{
// TODO >> Change this path based on your local system
var resourcePath = #"c:\project\github\PdfSignSamplePkcs1\Resources\";
var pdfToSignPath = Path.Combine(resourcePath, "test.pdf");
var signedPdfPath = Path.Combine(resourcePath, "signedPdf.pdf");
var privateKey = Path.Combine(resourcePath, "SignTest.pfx"); // not critical, self signed certificate
var privateKeyPassword = "test";
// ############
// Change value in order to create the PKCS7
// either manually or via Itext
// ############
bool createSignatureViaPlainPkcs1 = false;
//delete signed file if it exists
if (System.IO.File.Exists(signedPdfPath))
System.IO.File.Delete(signedPdfPath);
var pdfToSign = System.IO.File.ReadAllBytes(pdfToSignPath);
byte[] pdfDigest = null;
//#1 Prepare pdf for signing
var SignatureAttributeName = $"SignatureAttributeName_{DateTime.Now:yyyyMMddTHHmmss}";
byte[] preparedToSignPdf = null;
using (MemoryStream input = new MemoryStream(pdfToSign))
{
using (var reader = new PdfReader(input))
{
StampingProperties sp = new StampingProperties();
sp.UseAppendMode();
using (MemoryStream baos = new MemoryStream())
{
var signer = new PdfSigner(reader, baos, sp);
signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);
signer.SetFieldName(SignatureAttributeName);
DigestCalcBlankSigner external = new DigestCalcBlankSigner(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signer.SignExternalContainer(external, 32000);
//get digest to be signed
pdfDigest = external.PdfDigest;
preparedToSignPdf = baos.ToArray();
}
}
}
//#2 Create PKCS7
SignService ss = new SignService(pdfDigest, privateKey, privateKeyPassword);
byte[] signatureAsPkcs7 = null;
if (createSignatureViaPlainPkcs1)
signatureAsPkcs7 = ss.CreatePKCS7ViaPkcs1(); // >> Creates invalid pdf signature
else
signatureAsPkcs7 = ss.CreatePKCS7(); // Creates valid pdf signature
//#3 apply cms(PKCS7) to prepared pdf
ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signatureAsPkcs7);
using (MemoryStream preparedPdfStream = new MemoryStream(preparedToSignPdf))
{
using (var pdfReader = new PdfReader(preparedPdfStream))
{
using (PdfDocument docToSign = new PdfDocument(pdfReader))
{
using (MemoryStream outStream = new MemoryStream())
{
PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
System.IO.File.WriteAllBytes(signedPdfPath, outStream.ToArray());
}
}
}
}
}
}
}
Manual creation of the pkcs7 signature
In this sample, first create a PKCS1 signature using a local certificate. The created PKCS1 signature then is applied to the PdfPKCS7 container via SetExternalDigest
The pdf created in this way is invalid.
public byte[] CreatePKCS7ViaPkcs1()
{
//Load the certificate used for signing
signCertificatePrivateKey = LoadCertificateFromFile();
// create sha256 message digest
// This is from https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7
// Not sure if this is required, but the created signature is invalid either way
using (SHA256 sha256 = SHA256.Create())
{
Digest = sha256.ComputeHash(Digest);
}
//Create pkcs1 signature
byte[] signature = null;
using (var key = signCertificatePrivateKey.GetRSAPrivateKey())
{
signature = key.SignData(Digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(signCertificatePrivateKey);
PdfPKCS7 sgn = new PdfPKCS7(null, new[] { cert }, "SHA256", false);
sgn.SetExternalDigest(signature, null, "RSA");
//Return the complete PKCS7 CMS
return sgn.GetEncodedPKCS7(Digest, PdfSigner.CryptoStandard.CMS, null, null, null);
}
Create PKCS7 signature using PrivateKeySignature implementation
In this sample, the PKCS7 is created using iText PrivateKeySignature. The signature is created with the same digest and the same private key as in the other example.
The pdf created here is valid. But since this approach doesn't allow the use of an external service for creating the signature, i cannot use it.
public byte[] CreatePKCS7ViaPkcs1()
{
//Load the certificate used for signing
signCertificatePrivateKey = LoadCertificateFromFile();
// create sha256 message digest
// This is from https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7
// Not sure if this is required, but the created signature is invalid either way
using (SHA256 sha256 = SHA256.Create())
{
Digest = sha256.ComputeHash(Digest);
}
//Create pkcs1 signature using RSA
byte[] signature = null;
using (var key = signCertificatePrivateKey.GetRSAPrivateKey())
{
signature = key.SignData(Digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(signCertificatePrivateKey);
PdfPKCS7 sgn = new PdfPKCS7(null, new[] { cert }, "SHA256", false);
sgn.SetExternalDigest(signature, null, "RSA");
//Return the complete PKCS7 CMS
return sgn.GetEncodedPKCS7(Digest, PdfSigner.CryptoStandard.CMS, null, null, null);
}
Remark (edit)
I think the reason for the problem is that i don't use GetAuthenticatedAttributeBytes for getting the hash to be signed. But i cannot use this method. The timestamp, ocsp and CLR are returned as part of the service call. Since the parameters for GetAuthenticatedAttributeBytes must be the same as when the signature is applied, i guess i cannot use this functionality.
Question
What is the reason the signature created via RSA is invalid in the resulting pdf?
edit: To be more specific: How can i create a valid pkcs7 container when the signature service returns PKCS1, Timestamp, Ocsp and CRL. What exactly must be signed in this case?
One error is fairly obvious:
In CreatePKCS7 you sign the to-be-signed attributes of the signature container (GetAuthenticatedAttributeBytes) which contain the document digest (Digest):
var sh = sgn.GetAuthenticatedAttributeBytes(Digest, PdfSigner.CryptoStandard.CMS, null, null);
byte[] extSignature = signature.Sign(sh);
In CreatePKCS7ViaPkcs1 you sign the document digest (Digest) itself:
//Create pkcs1 signature using RSA
byte[] signature = null;
using (var key = signCertificatePrivateKey.GetRSAPrivateKey())
{
signature = key.SignData(Digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
In both cases you continue by injecting the returned signature into a PdfPKCS7:
sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
and
sgn.SetExternalDigest(signature, null, "RSA");
respectively.
That the first variant works, is an indicator telling you that SetExternalDigest expects as first parameter the signature (externally signed digest) of the to-be-signed attributes, not of the document digest directly.
Thus, in CreatePKCS7ViaPkcs1 you simply sign the wrong bytes!
You can fix CreatePKCS7ViaPkcs1 by (just like in CreatePKCS7) creating the PdfPKCS7 instance before and receiving the to-be-signed attributes from it (using GetAuthenticatedAttributeBytes). You may or may not have to hash the result hereof before signing - I'm not so well-versed in .NET crypto APIs.

Encrypt and Decrypt using PFX on multiple servers

I'm currently attempting to build a system which encrypts customer data and adds it to a remote MSMQ queue which is on another server. The data is then picked up by a job that runs every X Minute which will attempt to decrypt the data and processes it.
There is a requirement of us having to use .PFX Certificate to do the encryption/decryption (which I am aware is not be most efficient way of doing things but the requirement is there and I am unable to get this changed).
I am currently using a self-signed certificate using Open-SSL using:
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
openssl pkcs12 -inkey key.pem -in certificate.pem -export -out certificate.p12
I have been successful at encrypting the data but every time I attempt a decrypt, I get the generic "The parameter is incorrect" exception.
To load the certificate, we have the .PFX file saved locally on the machines and using the X509Certificate2 class, we import it and use it to encrypt and decrypt. Here is a simplified version of the helper class that I am working with:
public static string EncryptData(string data)
{
var certificate = GetCertificate();
using (var rsa = certificate.PublicKey.Key as RSACryptoServiceProvider)
{
var dataBytes = Convert.FromBase64String(data);
var encryptedBytes = rsa.Encrypt(dataBytes, false);
return Convert.ToBase64String(encryptedBytes);
}
}
public static string DecryptData(string data)
{
var certificate = GetCertificate();
using (var rsa = certificate.PrivateKey as RSACryptoServiceProvider)
{
var dataBytes = Convert.FromBase64String(data);
var decryptedBytes = rsa.Decrypt(dataBytes, false);
return Convert.ToBase64String(decryptedBytes);
}
}
private static X509Certificate2 GetCertificate()
{
var certificate = new X509Certificate2();
certificate.Import("certificatePath", "certificatePassword", X509KeyStorageFlags.PersistKeySet);
return certificate;
}
The error always occurs on the "rsa.Decrypt()" call.
I have attempted the following:
Call "rsa.Decrypt()" in my "EncryptData" method right after encryption. This works with no issue and the "rsa.Decrypt" gives the me same bytes as the original data bytes.
Call "DecryptData" method straight after "EncryptData" call. The same issue occurs and I get Exception with "The parameter is incorrect"
This is why I suspect the fact a "new" X509Certificate2 is created that the private key is no longer the same and can no longer decrypt the data.
Please note that I am no security expert and have not worked with X509 Certificates or any cryptography for that matter so I am a bit out of my depth and may be doing something really silly so please let me know if I am.
Update 1 (08/03/2019)
Have updated the code code as per recommendation points 1-3 & 5 given by #bartonjs
public static string EncryptData(string data)
{
var certificate = GetCertificate();
using (var rsa = certificate.GetRSAPublicKey())
{
var dataBytes = Convert.FromBase64String(data);
var encryptedBytes = rsa.Encrypt(dataBytes, RSAEncryptionPadding.OaepSHA1);
return Convert.ToBase64String(encryptedBytes);
}
}
public static string DecryptData(string data)
{
var certificate = GetCertificate();
using (var rsa = certificate.GetRSAPrivateKey())
{
var dataBytes = Convert.FromBase64String(data);
var decryptedBytes = rsa.Decrypt(dataBytes, RSAEncryptionPadding.OaepSHA1);
return Convert.ToBase64String(decryptedBytes);
}
}
private static X509Certificate2 GetCertificate()
{
var certificate = new X509Certificate2("certificatePath", "certificatePassword", X509KeyStorageFlags.PersistKeySet);
return certificate;
}
Added error message:
Message: The parameter is incorrect.
Stack Trace:
at System.Security.Cryptography.NCryptNative.DecryptData[T](SafeNCryptKeyHandle key, Byte[] data, T& paddingInfo, AsymmetricPaddingMode paddingMode, NCryptDecryptor`1 decryptor)
at System.Security.Cryptography.NCryptNative.DecryptDataOaep(SafeNCryptKeyHandle key, Byte[] data, String hashAlgorithm)
at System.Security.Cryptography.RSACng.Decrypt(Byte[] data, RSAEncryptionPadding padding)
I'm sorry to say that pretty much everything here is wrong.
GetCertificate
private static X509Certificate2 GetCertificate()
{
var certificate = new X509Certificate2();
certificate.Import("certificatePath", "certificatePassword", X509KeyStorageFlags.PersistKeySet);
return certificate;
}
You're importing with PersistKeySet, so you're slowly filling up your hard drive. See What is the rationale for all the different X509KeyStorageFlags?
Also, you're using certificate.Import, which is not available in .NET Core (because mutating X509Certificate2 objects is unexpected). Just use the constructor. So this whole method should be
private static X509Certificate2 GetCertificate()
{
// Assuming you do nothing else with the certificate than what's shown here,
// EphemeralKeySet will work for you (except on macOS).
return new X509Certificate2(path, password, X509KeyStorageFlags.EphemeralKeySet);
}
EncryptData
public static string EncryptData(string data)
{
var certificate = GetCertificate();
using (var rsa = certificate.PublicKey.Key as RSACryptoServiceProvider)
{
var dataBytes = Convert.FromBase64String(data);
var encryptedBytes = rsa.Encrypt(dataBytes, false);
return Convert.ToBase64String(encryptedBytes);
}
}
There are a couple of things wrong here.
1) PrivateKey is a shared property, so if it got read more than once you'd be disposing the object out from under another caller.
2) You're not disposing it if you happened to get a non-RSA certificate
3) You're using PrivateKey, which does not support the better RSA or DSA classes that support modern options.
4) You're the sole handler of the certificate, but didn't dispose it. Maybe your ownership semantics could be more clear.
From a security perspective, also 5) you're using PKCS#1 padding instead of OAEP
From a data perspective, also 6) why is encrypt being given base64 data instead of the raw data?
I won't address #s 4-6.
public static string EncryptData(string data)
{
var certificate = GetCertificate();
using (RSA rsa = certificate.GetRSAPublicKey())
{
var dataBytes = Convert.FromBase64String(data);
var encryptedBytes = rsa.Encrypt(dataBytes, RSAEncryptionPadding.Pkcs1);
return Convert.ToBase64String(encryptedBytes);
}
}
In this case it's correct to put it the private key in a using statement, the GetRSAPrivateKey() method always returns a new object.
DecryptData
Decrypt should be altered similarly to Encrypt.
If, after all of that, you're still getting exceptions, please include the exact message and the stack trace (at least the portions from the call to RSA.Decrypt through where it was thrown)

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.

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();
}
}

C# RSA implementation with OpenSSL keys & Bouncy castle

I'm trying to implement string encryption-decryption in C# using OpenSSL-generated keypair and Bouncy Castle.
OpenSSL granted me keypair, which I have separated in 2 files. Now I am using Pemreader from Bouncy Castle to read the keys and change them to AsymmetricKeyParameters.
The code below runs, but the decrypted string isn't the same as the original - I am getting a bunch of ?'s.
If I print out the keys, they seem just like in the text file.
Could someone point out what I am doing wrong? The pemreading procedure or engine-using seem to be the cause. How strong this encryption will be with 2048-bit key without padding?
string test = "qwerty12345";
AsymmetricKeyParameter keyparmeter = readPublicKey(public_path); // Read public key into string
/* Print the test key */
Console.WriteLine("test key = " + test);
/* Convert test to byte array */
byte[] bytes = new byte[test.Length * sizeof(char)];
System.Buffer.BlockCopy(test.ToCharArray(), 0, bytes, 0, bytes.Length);
byte[] cipheredbytes = null;
/* Initiate rsa engine */
RsaEngine e = new RsaEngine();
e.Init(true, keyparmeter); // initialize engine true, encrypting
/* Crypt! */
cipheredbytes = e.ProcessBlock(bytes, 0, bytes.Length);
// ## NOW DECRYPTION ##
/* Get the private key */
AsymmetricKeyParameter privkeyparameter = readPrivKey(privkey_path);
byte[] reversedbytes = null;
/* Initiate rsa decrypting engine */
RsaEngine d = new RsaEngine();
d.Init(false, privkeyparameter); // initialize engine false, decrypting
/* Decrypt! */
reversedbytes = d.ProcessBlock(cipheredbytes, 0, cipheredbytes.Length);
char[] chars = new char[cipheredbytes.Length / sizeof(char)];
System.Buffer.BlockCopy(cipheredbytes, 0, chars, 0, cipheredbytes.Length);
string reversedtest = new string(chars);
### PEMREADING ###
/* Convert PEM into AsymmetricKeyParameter */
private AsymmetricKeyParameter readPublicKey(string path_to_key)
{
RsaKeyParameters asmkeypar;
using(var reader = File.OpenText(path_to_key))
asmkeypar = (RsaKeyParameters) new PemReader(reader).ReadObject();
return asmkeypar;
}
/* Convert PEM into AsymmetricKeyParameter */
private AsymmetricKeyParameter readPrivKey(string path_to_key)
{
AsymmetricCipherKeyPair asmkeypar;
using (var reader = File.OpenText(path_to_key))
asmkeypar = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
return (RsaKeyParameters) asmkeypar.Private;
}
You are using the base RSA algorithm. This is also known as raw or textbook RSA. Basically it performs modular exponentiation, but it doesn't do the padding or unpadding. So what you are receiving is the plaintext + the zero's that have been put in front of the value, as the unpadding does not seem to take place.
Finally, you should perform character encoding instead of System.Buffer.BlockCopy, the latter will probably make a mess out of it because it has to operate on a Unicode encoded string in .NET.
I can refer you to this question on crypto that tries to list all the possible attacks on raw/textbook RSA. There are a lot, the chance that your code is secure is about zero.

Categories