Why is SignHash from RSACng (dotNetCore) different from BoncyCastle (running in Mono)? - mono

I'm trying summarize: I have a self-signed certificate (created by OpenSSL) being used in a aspnetCore API to verify hash (or message digest, if you prefer) signature. I can run tests to sign the hash and run in the API and everything goes well.
Now, I'm trying do my client sign such hashs and consume the API. It's an application running on Mono. I'm tried do exactly the same thing as my tests, but Mono has a bug in X509Certificate2, when pfx certificate is protected by password. So, I replaced it the by the famous BouncyCastle. However, the results are different... Checking the pk algorithm, I can see some differences, but nothing so remarkable (at least to me).
Can you give me advices? I gonna put the codes:
Good code running on tests (dotNet Core):
Console.WriteLine("Entry text:");
var text = Console.ReadLine();
X509Certificate2 certificate = new X509Certificate2((byte[])Resource1._2dbb1721_281d_4990_836c_7e46909b8767, "1509a96c-d56b-4e17-af5b-c11f5f214c74");
SHA1Managed sha1Hasher = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1Hasher.ComputeHash(data);
RSA provider = (RSA)certificate.PrivateKey;
var signatureData = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
var signature = Convert.ToBase64String(signatureData);
Bad code, running on Mono and BouncyCastle:
SHA1Managed sha1Hasher = new SHA1Managed();
var encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(uuid);
byte[] hash = sha1Hasher.ComputeHash(data);
var rsaParameters = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)privateKeyParam);
var provider = RSA.Create();
provider.ImportParameters(rsaParameters);
var signatureData = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
var signature = Convert.ToBase64String(signatureData);
Both certificates are the same. Debugging, I could see the dotNet instantiates on provider as RSACng. The SignatureAlgorithm property is "RSA" as the KeyExchangeAlgorithm so.
In the BoncyCastle over Mono, the provider is RSACrytoServiceProvider. According some articles that I read, this shouldn't make any difference. However, SignatureAlgorithm is "http://www.w3.org/2000/09/xmldsig#rsa-sha1", while KeyExchangeAlgorithm is "RSA-PKCS1-KeyEx".
Thanks in advance

My bad: I've just realized my program wasn't using the string it is supposed might use to calculate the hash...
The Mono's version using BouncyCastle is working perfect fine.

Related

"ERROR unspecified algorithm" when signing a PDF with itextsharp 5.5.13.2 and SHA-256

Good morning, I am trying to sign a PDF document using ItextSharp 5.5.13.2 and SHA-256 for the signature process but at the moment of signing I get the error "Specified Algorithm is Invalid". This error does not happen when I use SHA-1 with .NetFramework 4.7.2 to sign the PDF, the next method is the one I use to sign the PDF.
public void SignPDF(string PathSourceDoc, string PathTargetDoc, X509Certificate2 certificate, string pathLogo)
{
using (PdfReader reader = new PdfReader(PathSourceDoc))
using (var writer = new FileStream(PathTargetDoc, FileMode.Create, FileAccess.Write))
using (var stamper = PdfStamper.CreateSignature(reader, writer, '\0', null, true))
{
var signature = stamper.SignatureAppearance;
signature.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
signature.Reason = "My Reason";
signature.Location = "My Location";
signature.SignDate = DateTime.Now;
signature.Acro6Layers = true;
PdfSignature objSignature = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
objSignature.Date = new PdfDate(signature.SignDate);
signature.CryptoDictionary = objSignature;
var bcCert = DotNetUtilities.FromX509Certificate(certificate);
string name = CertificateInfo.GetSubjectFields(bcCert).GetField("CN");
string industry = CertificateInfo.GetSubjectFields(bcCert).GetField("O");
string position = CertificateInfo.GetSubjectFields(bcCert).GetField("T");
DateTime date = DateTime.Now;
signature.Layer2Text = "Digital Signed by: " + name + "\n" +
"Reason: " + "My Reason" + "\n" +
"Date: " + date;
signature.Layer2Font = new Font(iTextSharp.text.Font.FontFamily.TIMES_ROMAN, 8);
Image img = Image.GetInstance(pathLogo);
signature.SignatureGraphic = img;
signature.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
Rectangle rect = new Rectangle(50, 50, 300, 110);
signature.SetVisibleSignature(rect, 1, null);
var standard = CryptoStandard.CADES;
/*I tried this way but I get an error of type Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.'*/
X509Certificate cert = certificate;
X509Certificate2 signatureCert = new X509Certificate2(cert);
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private;// the error is generated in this line (Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.').
IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");
/****************************************************************************************************************************************************************/
X509Certificate2Signature externalSignature = new X509Certificate2Signature(certificate, DigestAlgorithms.SHA1); /*throws an exception on this line of type "System.ArgumentException: 'Unknown encryption algorithm System.Security.Cryptography.RSACng'"
* when using .NET Core 3.1. this always happens, it doesn't matter if I use sha1 or use any other algorithm, it always generates error*/
MakeSignature.SignDetached(signature, externalSignature, new[] { bcCert }, null, null, null, 0, standard);
}
}
The method receives as input parameters the path of the PDF that I need to sign, the path of the pdf that will be created when signing, the certificate and the path of the logo for displaying the image in the signature. Actually I don't know what I'm doing wrong since I've been researching related questions and it should work with SHA-1 and SHA-256 and .NetFramework.
Then I migrated the project to .NetCore 3.1 in order to try to fix the problem but instead I got a new error (It is commented in the code). My goal is to use .NetCore and to allow me to sign a pdf using sha256. I have no problem modifying the SignPdf method in order to make the program work.
Any contribution or information link is appreciated. Thanks for your help.
Psdt: This is the stacktrace is as follows...
this is the stacktrace image of the error when the project was migrated to .Net Core 3.1
Apparently the Microsoft Crypto APIs used by X509Certificate2Signature do not support SHA-256.
You use a X509Certificate2Signature instance as IExternalSignature argument of MakeSignature.SignDetached.
If the PrivateKey of your X509Certificate2 is a RSACryptoServiceProvider (which appears to be the case for your certificate), X509Certificate2Signature.Sign calls
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
return rsa.SignData(message, hashAlgorithm);
RSACryptoServiceProvider.SignData at first apparently does support "SHA256" because before forwarding execution to SignHash it must have calculated the hash value to sign.
RSACryptoServiceProvider.SignHash, though, does not support SHA-256, according to the Microsoft docs on that method:
Remarks
This method creates a digital signature that is verified using the VerifyHash method.
The valid hash algorithms are SHA1 and MD5. The algorithm identifier can be derived from the hash name by using the MapNameToOID method.
Due to collision problems with SHA1 and MD5, Microsoft recommends a security model based on SHA256 or better.
(Viewed 2021-06-24, 11:07)
Thus, you get the "Specified Algorithm is Invalid" error.
Essentially, therefore, one should not base one's IExternalSignature implementation for RSA signing on RSACryptoServiceProvider anymore.
Maybe this is why X509Certificate2Signature was not ported to iText version 7...
An alternative approach might be to check whether the PrivateKey is a RSACng first and in that case use it as a RSACng. This Cryptography Next Generation (CNG) implementation of the RSA algorithm should support better hashing algorithms...

Keyset does not exist /r /n

I was tasked to create a integration service between our SharePoint app and one service provider. One requirement of the service provider I'm going to integrate with is to provide them a public key which they will use to verify my request which was signed using our own private key.
Initially I created a console app which reads the certificate store and gets the private key which to use to sign my request and all. The console app works fine so I decided to move it now within our SharePoint application. Unfortunately it fails in this specific part of the code:
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
The whole code snippet which gets the certificate and does the signing can be found below:
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, "thumbprinthere", true);
if (certs.Count > 0)
{
privateCert = certs[0];
}
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
byte[] sig = key.SignData(Encoding.ASCII.GetBytes(data), CryptoConfig.MapNameToOID("SHA256"));
string signature = Convert.ToBase64String(sig);
[UPDATE]
I tried following the steps in this link. I first uninstalled my existing private key in the server. I then imported it back to the Certificate store and confirmed that there was a Thumbprint property. After that, I ran findprivatekey.exe and was able to navigate to the MachineKeys folder. From there I added different users ranging from Network Services, IIS_IUSRS and even local accounts I used to login to the server as well as SPFarm admin but I still keep getting the error.
I also made sure that the key I added was exportable so there should be a way for it the application to extract the private key attached to the certificate.
[UPDATE 2]
I updated the code so that it just returns one certificate prior to assigning it to the variable I was using to extract the private key. Still the same issue even if I can see that the certs variable is returning exactly one record.
After much checking I realized I missed one important part in calling the method code block above. I forgot to wrap it an elevate privilege block. After doing that, the code functioned similarly as my console app.
SPSecurity.RunWithElevatedPrivileges(delegate())
{
...
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, "<thumbprinthere>", true);
if (certs.Count > 0)
{
privateCert = certs[0];
}
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
byte[] sig = key.SignData(Encoding.ASCII.GetBytes(data), CryptoConfig.MapNameToOID("SHA256"));
string signature = Convert.ToBase64String(sig);
...
});

How to build a RFC 3161 time stamp using Bouncy Castle

we need to implement our own PDF timestamping mechanism based on X509 certificate (including private key of course) and RFC 3161. I've googled and asked here on SO and proper solution would be to re-implement TSAClient class to do timestamping locally for us (without online TSA). However I didn't find any implementation of RFC 3161 except SecureBlackbox components. It should be possible with Bouncy Castle libraries but I don't know how.
Can you please point me to right direction?
It is possible to generate a RFC3161 timestamp token with Bouncycastle libraries.
First create a TimestampRequest. For your case it is only a wrapper for the digest algorithm and the digest value.
byte[] document = /* ... */
byte[] digest = MessageDigest.getInstance("SHA256").digest(document);
TimeStampRequestGenerator tsReqGen = new TimeStampRequestGenerator();
TimeStampRequest tsReq = tsReqGen.generate(CMSAlgorithm.SHA256, digest);
Then generate the token
DigestCalculator dgCalc = new JcaDigestCalculatorProviderBuilder().build();
ContentSigner signer = new JcaContentSignerBuilder().build(getPrivateKey());
SignerInfoGenerator siGen = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder()).build(signer, getCertificate());
ASN1ObjectIdentifier policy = new ASN1ObjectIdentifier("1.2.3.4.5.6"); // Replace by your timestamping policy OID
TimeStampTokenGenerator tstGen = new TimeStampTokenGenerator(siGen, dgCalc, policy);
/* Set the parameters e.g. set the accuracy or include the signing certificate */
TimeStampToken tst = tstGen.generate(tsReq, generateSerialNumber(), new Date());
byte[] encoding = tst.getEncoded();

Security with RijndaelManaged and ServicePointManager

I have a security question about RijndaelManaged and
ServicePointManager.
I have implemented a system where C# application is encrypting data, such as user credentials and some XML data. Then I use WebClient to send encrypted user credentials with some encrypted XML document containing instructions - to my Tomcat Java Web application. The job of the Java Application: is to decrypt user credentials and XML instructions – perform instructions and respond back to C# with an encrypted XML result.
All connections from my C# application to Tomcat server are with SSL enabled (Self signed certificate for now).
First Question: Given the fact that my C# application by default always connecting to my Server (only) with SSL enabled. Can I simply implement the call back function as:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
As I understand that the call back function is used to Identify and validate certificate used by the Server I’m connecting to. If I were to give that application to – say one of my clients to connect to my Server (with SSL enabled) – is the code above satisfactory? If client uses my application to connect to another server that is not known and I have no Idea about its SSL certificate status – the code above should be replaced with an actual certificate validation function. Does my question make sense?
Second Question: I have encryption/decryption implemented using RijndaelManaged in my C# application. But the key I’m using is part of the C# application – the application is obfuscated. As I understand this is not a secure way.
Is there a reliable way for the C# application to receive the encryption/decryption key from my Web application. Or is there a way for the key to be generated in C# application that can be used by Web application to decrypt the data – if so: how do I generate that key and most important how do I send it to the server in a reliable secure way. Since the connection is SSL – can the key simply be a part of the encrypted stream?
Here is code that I’m using for encryption in my C# app.
private const string KEY = "samplekey";
private const int KEY_SIZE = 128;
private const int KEY_BITS = 16;
private string Encrypt(string textToEncrypt)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.PKCS7;
rijndaelCipher.KeySize = KEY_SIZE;
rijndaelCipher.BlockSize = KEY_SIZE;
byte[] pwdBytes = Encoding.UTF8.GetBytes(KEY);
byte[] keyBytes = new byte[KEY_BITS];
int len = pwdBytes.Length;
if (len > keyBytes.Length)
{
len = keyBytes.Length;
}
Array.Copy(pwdBytes, 0, keyBytes, 0, len);
rijndaelCipher.Key = keyBytes;
rijndaelCipher.IV = keyBytes;
ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt);
return System.Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length));
}

How to generate a CMS (cryptographic message syntax) with Mono Framework and BouncyCastle API?

I have a Certificate in PKCS#12 format and I need to generate a CMS signature (Cryptographic Message Syntax). Due that "Mono Framework" does not have a full implemented "System.Security.Cryptography" assembly, I am trying to use "Bouncy Castle API for C#".
So, using "Bouncy Castle", I need to write an alternative code to the one I had wrote on DOTNET.
The code on DOT NET is the following:
X509Certificate2 crt = new X509Certificate2();
byte[] crtBytes = [ certificate in the format PKCS12 (certificate + private key) obtained using FileStream class]
crt.Import(crtBytes, "123456", X509KeyStorageFlags.DefaultKeySet);
Encoding msgCodificado = Encoding.UTF8;
byte[] msgBytes = msgCodificado.GetBytes(xmlTRA.OuterXml); // xmlTRA.OuterXml is the data to sign
ContentInfo pkcsContentInfo = new ContentInfo(msgBytes);
SignedCms cms = new SignedCms(pkcsContentInfo);
CmsSigner firmante = new CmsSigner(crt);
firmante.IncludeOption = X509IncludeOption.EndCertOnly;
cms.ComputeSignature(firmante); // ---> throw an cryptografy exception with MONO
byte[] firma = cms.Encode();
firmaB64 = Convert.ToBase64String(firma);
Anyone knows how to write an alternative code using "Bouncy Castle API for C#"?
Org.BouncyCastle.Pkcs has classes for working with a PKCS#12 store.
Org.BouncyCastle.Cms has classes for working with CMS messages.
There are corresponding test classes in the source code that show the basics of using, e.g. Pkcs12Store(Builder) and CmsSignedData(Generator).