C# Sign file with Bouncy Castle Windows XP - c#

i have a project in C# where i have to sign the files making them .p7m with envelope cryptographic CAdES, i made it with BouncyCastle.
Now, the signing process it works perfectly on Windows 7 , 8 , 10 , but when i tested it on windows xp , there was an error
This is the function that sign files
SHA256Managed hashSha256 = new SHA256Managed();
byte[] certHash = hashSha256.ComputeHash(cert.RawData);
EssCertIDv2 essCert1 = new EssCertIDv2(new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier("2.16.840.1.101.3.4.2.1"), certHash);
SigningCertificateV2 scv2 = new SigningCertificateV2(new EssCertIDv2[] { essCert1 });
Org.BouncyCastle.Asn1.Cms.Attribute CertHAttribute = new Org.BouncyCastle.Asn1.Cms.Attribute(Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.IdAASigningCertificateV2, new DerSet(scv2));
Asn1EncodableVector v = new Asn1EncodableVector();
v.Add(CertHAttribute);
Org.BouncyCastle.Asn1.Cms.AttributeTable AT = new Org.BouncyCastle.Asn1.Cms.AttributeTable(v);
CmsSignedDataGenWithRsaCsp cms = new CmsSignedDataGenWithRsaCsp();
Org.BouncyCastle.Crypto.AsymmetricKeyParameter keyParameter = null;
dynamic rsa = (RSACryptoServiceProvider)cert.PrivateKey;
Org.BouncyCastle.X509.X509Certificate certCopy = DotNetUtilities.FromX509Certificate(cert);
cms.MyAddSigner(rsa, certCopy, keyParameter, "1.2.840.113549.1.1.1", "2.16.840.1.101.3.4.2.1", AT, null);
ArrayList certList = new ArrayList();
certList.Add(certCopy);
Org.BouncyCastle.X509.Store.X509CollectionStoreParameters PP = new Org.BouncyCastle.X509.Store.X509CollectionStoreParameters(certList);
Org.BouncyCastle.X509.Store.IX509Store st1 = Org.BouncyCastle.X509.Store.X509StoreFactory.Create("CERTIFICATE/COLLECTION", PP);
cms.AddCertificates(st1);
FileInfo File__1 = new FileInfo(NomeFile);
CmsProcessableFile file__2 = new CmsProcessableFile(File__1);
CmsSignedData Firmato = cms.Generate(file__2, true);
byte[] Encoded = Firmato.GetEncoded();
RisFirma = "";
return Encoded;
That is the windows xp error
Help is much appreciated. Kind Regards

It looks like the SHA-256 hash or the OID of the SHA-256. Try and update XP to the latest service pack; XP is old, so it may not support all algorithms out of the box. The idea of creating signatures on XP machines should be avoided; you should not create security on a platform without support or bug fixes.

I solved the problem, it seems that the latest CSP doesn't work with windows xp, i tryed with another CSP and the function start to work again.
bye

Related

Combining Public Certificate with Private RSA Key in C#

I received from a client public certificate and private RSA key, as follows (only partially displayed here):
-----BEGIN CERTIFICATE-----
MIIFKDCCAxACAQEwDQYJKoZIhvcNAQELBQAwbjELMAkGA1UEBhMCVVMxETAPBgNV
BAgMCElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRMwEQYDVQQKDApDbG91ZFF1
YW50MSUwIwYJKoZIhvcNAQkBFhZjcWFpb3BzQGNsb3VkcXVhbnQuY29tMB4XDTIw
MDkyMzIwMTgyN1oXDTQwMDkxODIwMTgyN1owRjELMAkGA1UEBhMCVVMxETAPBgNV
iESEKmYvylUwce7TcOuVnLtufyXxr8egu43jPvWDHsK0QvhMbx0q2KvyxGneQJ5E
...........
U0oIrq7M0qZTAf1BXEw9wgfQlIKfLzWDbIYKIg==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAuxHk8lBZaqRTzhi/jvlj59pehTkK/u2fVHLuHWZPGOvPiAjq
HqrAgM1urPpmQPC2QuWObmhvQ1uluo/tq6V56WZNUEYALZnXhGvVNrvnYfoFooI0
F+1dJI2xQ8AC11Khinj+yAPtKymMhZFOHzQaFnGvjhKWTn/I0MaoJc+TQ8JKbDQ0
ycvopM2finujmIb62cxxhkhkEC5T+yMUJ8MbCnfmWpYux/oU2CXSHrnPympCbx7x
cReH0BeZEZPMI7+1Yi7U+XKGc7RP+zt9AoIBAQDE0BZb3WfertNqmyj9TF73VAwq
SDtSa6MTC8bOdtQewCz9/zC13MSI+jZGRDKSh7nxNL+bgM0OgmM6n/5B/61SIRjM
hswgOU9AHewIFSB/5C/ZcxWqM+PrgYXXYfOl9ZeWs1x+YRKuqk/CW/Z2rHJXykNx
.............
czG95J+2TWdGAjPuFLA596PRXT5KN2ITOWXUym3UksHmonbJ9om+k0ckPr4J
-----END RSA PRIVATE KEY-----
Initially, I scanned many postings here and realized that the easiest way to combine the puplic PEM certificate with private RSA key (PEM format) is by using .NET 5:
// Needs .NET 5, that is .NET Core and VS2019 16.8 or higher
X509Certificate2 X509Cert = X509Certificate2.CreateFromPemFile("cert.cer", "cert.key");
X509Certificate2 cert = new X509Certificate2(mX509Cert.Export(X509ContentType.Pfx, "12345678"), "12345678");
This worked great.
However, later on I realized that the type of project I am targeting (VSTO Excel Add-In) is not supporting .NET 5 yet...
Therefore, I changed the above code to the following, (which also worked) not realizing that it also requires .NET 5...
X509Certificate2 pubOnly = new X509Certificate2("cert.cer");
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(keyBuffer, out _);
X509Certificate2 pubPrivEphemeral = pubOnly.CopyWithPrivateKey(rsa);
X509Certificate2 cert = new X509Certificate2(pubPrivEphemeral.Export(X509ContentType.Pfx, "12345678"), "12345678");
Then when I finally switched to .NET 4.7.2 and now 4.8 I was looking for compatible way to do the same, and tired several methods offered here, but in vain, none did not work - they had complex routines that threw exceptions...
So I am trying now BouncyCastle that helped a lot in the past and came up with this code:
string cerFilePath = Path.Combine(InstallDir, "cert.cer");
X509Certificate2 dotnetCertificate2 = new X509Certificate2(cerFilePath);
string keyFilePath = Path.Combine(InstallDir, "cert.key");
var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(File.OpenText(keyFilePath));
var pemObject = pemReader.ReadObject();
var rsa = DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)pemObject);
Unfortunately, the last line throws exception - there is something incompatible here and I do not know what it is... The exception message states:
"Unable to cast object of type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair' to type 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters'."
So if anyone can either correct me with the above BouncyCastle code or show me pure .NET 4.6 - 4.8 code - I'd really appreciate.
EDIT:
So I did find another code from here
private X509Certificate2 CreateCertWithKey()
{
using (Log.VerboseCall())
{
X509Certificate2 x509Cert = null;
try
{
string cerFilePath = Path.Combine(InstallDir, "cert.cer");
using (TextReader tr = new StreamReader(cerFilePath))
{
publicPemCert = tr.ReadToEnd();
}
string keyFilePath = Path.Combine(InstallDir, "cert.key");
using (TextReader tr = new StreamReader(keyFilePath))
{
privatePemKey = tr.ReadToEnd();
}
var keyPair = (AsymmetricCipherKeyPair)new PemReader(new StringReader(privatePemKey)).ReadObject();
var cert = (Org.BouncyCastle.X509.X509Certificate)new PemReader(new StringReader(publicPemCert)).ReadObject();
var builder = new Pkcs12StoreBuilder();
builder.SetUseDerEncoding(true);
var store = builder.Build();
var certEntry = new X509CertificateEntry(cert);
store.SetCertificateEntry("", certEntry);
store.SetKeyEntry("", new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });
byte[] data;
using (var ms = new MemoryStream())
{
//store.Save(ms, Array.Empty<char>(), new SecureRandom());
data = ms.ToArray();
x509Cert = new X509Certificate2(data);
}
}
catch(Exception ex)
{
Log.Verbose(ex);
}
return x509Cert;
}
}
It runs without problem but with the generated certificate my HttpWebRequest does not work, claiming that TSL/SSL connection cannot be created (and I tries 1.1, 1.2, and 1.3, although I recall that just Tls negotiates the version by itself...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
The .NET 5 code ran without a problem with exactly the same code for HttpWebRequest...
Just came across this while working through similar issues and I found a way to create the X509Certificate2 object that includes the private key (which I use for XML Signing). It requires that you create a PFX record using the public/private keys, I used OpenSSL:
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx
You can either open just instantiate the instance directly with the file name:
var cert = new X509Certificate2(filePath);
Or if you have already opened the file somewhere and stored it in base64 format, you can use the bytes:
var bytes = Convert.FromBase64String(pfx_base64);
var cert = new X509Certificate2(bytes);
A lot of credit for this goes to reading Scott Brady's blog.

PlatformNotSupportedException when establishing a TCP connection

After upgrading to Visual Studio 2019 I found that my existing Xamarin application wasn't able to establish TCP connections anymore.
After some research I found that Visual Studio 2019 also came with MSBuild 16 when VS2017 had MSBuild 15.
before I was using this piece of code after generating a certificate and key pair for my RSA system cryptography:
var cert = new X509Certificate2(DotNetUtilities.ToX509Certificate(bcCert))
{ PrivateKey = DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private) };
With Visual Studio 2019 you can't set the private key. This can be seen in the decompiled X509Certificate2.cs in
C:\ProgramFiles(x86)\MicrosoftVisualStudio\2019\Professional\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v1.0\System.dll
Following this repo I found a ConvertBouncyCert method that would return an X509Certificate2 wrapper around my generated X509Certificate which would have the private key attached but I was still getting an unable to decode exception.
Jeremy's library contains this code for converting an X509Certificate into an X509Certificate2:
var pkcs12Store = new Pkcs12Store();
var certEntry = new X509CertificateEntry(BouncyCert);
pkcs12Store.SetCertificateEntry(BouncyCert.SerialNumber.ToString(), certEntry);
pkcs12Store.SetKeyEntry(BouncyCert.SerialNumber.ToString(),
new AsymmetricKeyEntry(KeyPair.Private), new[] { certEntry });
X509Certificate2 keyedCert;
using (MemoryStream pfxStream = new MemoryStream()) {
pkcs12Store.Save(pfxStream, null, new SecureRandom());
pfxStream.Seek(0, SeekOrigin.Begin);
keyedCert = new X509Certificate2(pfxStream.ToArray(), "", X509KeyStorageFlags.Exportable);
}
return keyedCert;
You need to create a Pkcs12StoreBuilder object and set the DER encoding to true.
var pkcs12StoreBuilder = new Pkcs12StoreBuilder();
pkcs12StoreBuilder.SetUseDerEncoding(true);
var pkcs12Store = pkcs12StoreBuilder.Build();
var certificateEntry = new X509CertificateEntry(bouncyCert);
This article does a great job at explaining DER and all the components around it within cryptography and RSA systems.

Add Certificate ocsp Authority Information Access and policies extensions in c#

I am issuing (with own Certificate Authority) a certificate in c# code (based on: .NET Core 2.0 CertificateRequest class)
In CertificateRequest, unable to add Certificate ocsp Authority Information Access (oid: 1.3.6.1.5.5.7.1.1) and certificate policies (oid: 2.5.29.32) extensions (similar results of: Authority Information Access extension)
I do not want to use external libraries, perhaps only ASN1 libraries if needed.
Anyone can help with c# code to add these extensions as I didn't find any suitable types in .Net?
certificateRequestObject.CertificateExtensions.Add(
new X509Extension("2.5.29.32", **[Authority Information Access text] to RawData?** , false));
[Authority Information Access text]
Authority Information Access 1.3.6.1.5.5.7.1.1
[1]Authority Info Access
Access Method=On-line Certificate Status Protocol (1.3.6.1.5.5.7.48.1)
Alternative Name:
URL=example.org
[2]Authority Info Access
Access Method=Certification Authority Issuer (1.3.6.1.5.5.7.48.2)
Alternative Name:
URL=example.org
Disclaimer: I do strongly believe that you should not roll own crypto/CA and use standard CA software to issue certificate since they are intended to solve this problem.
There is no built-in support for ASN encoding/decoding in .NET (including .NET Core), you have to use 3rd party libraries.
For ASN encoding you can use ASN.1 library I developed: Asn1DerParser.NET
And use for your particular case will be:
Byte[] encodedData = new Asn1Builder()
.AddSequence(x => x.AddObjectIdentifier(new Oid("1.3.6.1.5.5.7.48.1")
.AddImplicit(6, Encoding.ASCII.GetBytes("http://ocsp.example.com"), true))
.GetEncoded();
var extension = new X509Extension("1.3.6.1.5.5.7.1.1", encodedData, false);
and add extension item to your request. If you need to add more URLs, then add more SEQUENCE elements:
Byte[] encodedData = new Asn1Builder()
.AddSequence(x => x.AddObjectIdentifier(new Oid("1.3.6.1.5.5.7.48.1")
.AddImplicit(6, Encoding.ASCII.GetBytes("http://ocsp1.example.com"), true))
.AddSequence(x => x.AddObjectIdentifier(new Oid("1.3.6.1.5.5.7.48.1")
.AddImplicit(6, Encoding.ASCII.GetBytes("http://ocsp2.example.com"), true))
.GetEncoded();
var extension = new X509Extension("1.3.6.1.5.5.7.1.1", encodedData, false);
I needed to add an AIA (Authority Information Access) extension using dotnet also. It is super cool #Crypt32 shared the code from Asn1DerParser.NET. It made me curious. I started looking at other code like BouncyCastle and I didn't see any code that did the same thing. There is a AuthorityInformationAccess class, but I couldn't find any tests that created an AIA extension. Maybe the implementation isn't finished. I could dig further but I instead looked at the dotnet runtime code. While of course there isn't a dotnet AIA builder, there is a SubjectAlternativeNameBuilder]2 I could learn from. So, I did just that. Essensually it uses the AsnWriter to encapsulate the mechanics of building an ASN1 Sequence. Below is an example where I add two certificate authority issuers. Next steps would be to encapuslate this in an AIA Builder but here is an example.
The parts that I struggled with were ensuring when to call writer.Encode and writer.WriteEncodedValue. After an hour or so everything made sense.
#Guru_07, I believe this allows you to avoid third party code. Although it has been some time since you posted your question.
List<byte[]> encodedUrls = new List<byte[]>();
List<byte[]> encodedSequences = new List<byte[]>();
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2");
encodedUrls.Add(writer.Encode());
writer = new AsnWriter(AsnEncodingRules.DER);
writer.WriteCharacterString(
UniversalTagNumber.IA5String,
"http://ocsp.example.com",
new Asn1Tag(TagClass.ContextSpecific, 6)
);
encodedUrls.Add(writer.Encode());
writer = new AsnWriter(AsnEncodingRules.DER);
using (writer.PushSequence())
{
foreach (byte[] encodedName in encodedUrls)
{
writer.WriteEncodedValue(encodedName);
}
}
encodedSequences.Add(writer.Encode());
encodedUrls = new List<byte[]>();
writer = new AsnWriter(AsnEncodingRules.DER);
writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2");
encodedUrls.Add(writer.Encode());
writer = new AsnWriter(AsnEncodingRules.DER);
writer.WriteCharacterString(
UniversalTagNumber.IA5String,
"http://ocsp2.example.com",
new Asn1Tag(TagClass.ContextSpecific, 6)
);
encodedUrls.Add(writer.Encode());
writer = new AsnWriter(AsnEncodingRules.DER);
using (writer.PushSequence())
{
foreach (byte[] encodedName in encodedUrls)
{
writer.WriteEncodedValue(encodedName);
}
}
encodedSequences.Add(writer.Encode());
writer = new AsnWriter(AsnEncodingRules.DER);
using (writer.PushSequence())
{
foreach (byte[] encodedSequence in encodedSequences)
{
writer.WriteEncodedValue(encodedSequence);
}
}
var ext = new X509Extension(
new Oid("1.3.6.1.5.5.7.1.1"),
writer.Encode(),
false);

(WIN32: 1400 ERROR_INVALID_WINDOW_HANDLE) while Signing a CSR with a SmartCard

I am trying to generate a CSR from a smartcard using the CertEnroll::CX509CertificateRequestPkcs10 library. It works fine if I just run it at the beginning. However if I run the ADAL login flow before hand I get the following error.
CertEnroll::CX509CertificateRequestPkcs10::Encode: Invalid window handle. 0x80070578 (WIN32: 1400 ERROR_INVALID_WINDOW_HANDLE)
I looked into the error and it seems that it is cause when you call a window that no longer exist. Since I cant control what window the CertEnroll::CX509CertificateRequestPkcs10::Encode calls is there a way to clear the pointers or something to avoid this error?
for reference here is my enroll code
var request = new CX509CertificateRequestPkcs10();
request.Initialize(X509CertificateEnrollmentContext.ContextUser);
request.PrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_NONE;
request.PrivateKey.Length = 2048;
request.PrivateKey.ProviderName = "Microsoft Base Smart Card Crypto Provider";
request.PrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_SIGNING_FLAG;
request.PrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
request.PrivateKey.MachineContext = false;
if (!subjectName.StartsWith("CN="))
subjectName = $"CN={subjectName}";
var subjectEncoded = new CX500DistinguishedNameClass();
subjectEncoded.Encode(subjectName);
request.Subject = subjectEncoded;
request.Encode();
and here is my Authentication Code
result = authContext.AcquireTokenAsync(ResourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always));
result.Wait();
_userName = result.Result.UserInfo.DisplayableId;
return result.Result.AccessToken;
I was able to go around this by changing my Provider to the newer version and the KeySec to None (since this is required for the new Gen Storage provder):
request.PrivateKey.ProviderName = "Microsoft Smart Card Key Storage Provider";
request.PrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_SIGNING_FLAG;
request.PrivateKey.KeySpec = X509KeySpec.XCN_AT_NONE;

Unable to sign xml programatically using smart card

I have a problem signing xml programatically using smart card. When I pass pin code using popup dialog (when passPinAutomatically is set to false) it works, but when I pass pin code programatically I receive an error:
A smart card was detected but is not the one required for the current operation. The smart card you are using may be missing required driver software or a required certificate.
The code works on my local machine and on two other machines, but does not work on the production machine. The smart card reader is OMNIKEY 6121, the driver is up to date and everything seems to be fine.
"Smart Card" and "Certificate Propagation" services are running. I have searched the internet for this issue but all solutions in the posts do not work for me.
What could be the problem? I post a part of my source code below. You can see image of the error I receive. Please help, I am running out of time? Thank you.
//X509Certificate2 certificate ... certificate object
//string pin ... pinCode
CspParameters csp = new CspParameters(1, "Microsoft Base Smart Card Crypto Provider");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer | CspProviderFlags.UseExistingKey;
SecureString pwd = new SecureString();
foreach (var c in pin)
{
pwd.AppendChar(c);
}
csp.KeyPassword = pwd;
csp.KeyNumber = (int)KeyNumber.Signature;
RSACryptoServiceProvider rsa;
if (!passPinAutomatically)
{
rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
}
else
{
rsa = new RSACryptoServiceProvider(csp);
}
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("<signature></signature>");
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.KeyInfo = keyInfo;
signedXml.SigningKey = rsa;
Reference reference = new Reference();
reference.Uri = String.Empty;
XmlDsigEnvelopedSignatureTransform transform = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(transform);
signedXml.AddReference(reference);
signedXml.ComputeSignature();
string signedXmlText = signedXml.GetXml().InnerXml;
Console.WriteLine(signedXml);
Console.ReadLine();

Categories

Resources