Playing around with certificates in .net5 - bouncycastle

I am trying to learn about certificates, the need for that is from an idea I had a few days ago to simplify the ordering and delivery of SSL certificates to my collegues. So I startet to investigate "how hard can it be"?
And to large entusiasm, the entire process was "not so hard", but then I came to "generating pfx certificates", with private key...
The code is from a console test app:
Creating keys and storing it.
int keysize = 2048;
CngKeyCreationParameters ckcParams = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
KeyCreationOptions = CngKeyCreationOptions.None,
KeyUsage = CngKeyUsages.AllUsages,
};
ckcParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(KeySize), CngPropertyOptions.None));
CngKey myCngKey = CngKey.Create(CngAlgorithm.Rsa, KeyName, ckcParams);
byte[] privatePlainTextBlob = myCngKey.Export(CngKeyBlobFormat.Pkcs8PrivateBlob);
string privateblob_string = Convert.ToBase64String(privatePlainTextBlob);
// Now I can save the Pkcs8PrivateBlob "somewhere"
Later I can pick up this up and create a Certificate Request and send it to a certificate service by API.
byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);
request = new CertificateRequest(
new X500DistinguishedName(order.CertName),
rsa,
HashAlgorithmName.SHA512,
RSASignaturePadding.Pkcs1);
etc....
Now I can download the issued certificate in various formats from the API.
So far so good. Now I want to create PFX file and KEY file.
Creating PFX:
// My issued certificate from provider
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
// pfx
var certificate = new X509Certificate2(PublicKeyStr, string.Empty, X509KeyStorageFlags.Exportable);
byte[] certificateData = certificate.Export(X509ContentType.Pfx, "password");
// Now I have the pfx, but no private key
I am obviously is not capable of solving this. I have been on a journey into Bouncy Castle, with no luck (btw: where is their documentation?).
I have noticed that .net5 has a metode that I thought (again) might solve this.
X509Certificate2.CreateFromPem(ReadOnlySpan certpem, ReadOnlySpan keypem)
But then I need to get the keypem "private key pem".
My question is simple: Have I totally misunderstod or is the any way to add the needed private key to the pfx file with the information I have stored from the CngKey?
ANY suggestions, ideas, help, tips will be very welcomed. It is simply so frustrating to be so close and just fail miserably.

You need to associate the public certificate with the private key before exporting it as a PFX.
// Key
byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);
// Cert
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
var certificate = new X509Certificate2(PublicKeyStr);
// Together:
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa);
// PFX:
byte[] pfx = certWithKey.Export(X509ContentType.Pfx, pwd);

Related

How do I verify a key pair matches? (node-forge)

I need to make sure a client generated RSA key pair matches before signing it. I can't seem to find any documentation (npm:node-forge) on how to do so. I'm guessing I could sign something with it, and then verify the signature, but that's not efficient. I currently have this:
const Forge = require("node-forge");
try {
publicKey = Forge.pki.publicKeyFromPem(publicKey);
privateKey = Forge.pki.privateKeyFromPem(privateKey);
} catch(err) {
// ...
}
// ...
Any ideas are appreciated.
I've found my answer: I don't need to be sent the public key in the first place. You can build the public key from the private key like this:
// const privateKey = ...;
const publicKey = Forge.pki.setRsaPublicKey(privateKey.n, privateKey.e);
More information on this solution can be found here: Extract public key from private key pem using only nodejs/javascript.

Signature is Invalid after attaching certificate to the processed pdf document. i am unable to get if there is an issue in code or certificate

I have a signed pdf I am attaching a certificate(.pfx) to the document through itextsharp. Everything in the code is tested and working fine but when I download and open the pdf in acrobat reader it says the signature is not valid I have changed preferences tried almost every setting since yesterday but there isn't any luck.
two things I noticed in certificate detail that for its "intended" property: the DIGITAL signature is not mentioned whereas encrypt document etc is mentioned is this the reason it is not validating the document for signature.
and the second thing it says: certificate has error: not valid for usage
code for attaching certificate;
var pathCert =
Server.MapPath("..../App_Data/Certificates/.....sdd.pfx");
string Password = "**************";
var pass = Password.ToCharArray();
System.Security.Cryptography.X509Certificates.X509Store store =
new System.Security.Cryptography.X509Certificates.X509Store
(Cryptography.X509Certificates.StoreLocation.CurrentUser);
store.Open(System.Security.
Cryptography.X509Certificates.OpenFlags.ReadOnly);
string PfxFileName = pathCert;
string PfxPassword = Password;
System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new
System.Security.Cryptography.X509Certificates.X509Certificate2
(PfxFileName, PfxPassword, Security.Cryptography.X509Certificates.
X509KeyStorageFlags.MachineKeySet);
string SourcePdfFileName = "(Directory)/Desktop/tetsing/test.pdf";
string DestPdfFileName = "(Directory)/Desktop/tetsing/test_Signed.pdf";
Org.BouncyCastle.X509.X509CertificateParser cp = new
Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new
Org.BouncyCastle.X509.X509Certificate[] {
cp.ReadCertificate(cert.RawData) };
iTextSharp.text.pdf.security.IExternalSignature externalSignature = new
iTextSharp.text.pdf.security.X509Certificate2Signature(cert, "SHA-1");
PdfReader pdfReader = new PdfReader(SourcePdfFileName);
FileStream signedPdf = new FileStream(DestPdfFileName, FileMode.Create);
//the output pdf file
PdfStamper pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf,
'\0');
PdfSignatureAppearance signatureAppearance =
pdfStamper.SignatureAppearance;
signatureAppearance.Reason = "Signed Document";
signatureAppearance.Location = "Unknown";
signatureAppearance.SignatureRenderingMode =
PdfSignatureAppearance.RenderingMode.DESCRIPTION;
MakeSignature.SignDetached(signatureAppearance, externalSignature,
chain,
null, null, null, 0, CryptoStandard.CMS);
pdfReader.Close();
Adobe acrobat reader is very picky on the certificate key usage and intended purpose (Key Usage and Enhanced Key Usage) and other details of the certificate. Have you tried a certificate with Digital Signature as key usage and Code Signing as intended purpose?
Here is a blog post that shows how to self sign a certificate with that properties for doing signatures if you do not have access to a real publicly trusted signing certificate.
certificate has error: not valid for usage
According to the Adobe Digital Signatures Guide for IT, Adobe Acrobat accepts only
one or more of the following Key usage values (if any)
nonRepudiation
signTransaction (11.0.09 only)
digitalSignature (11.0.10 and later)
and one or more of the following Extended key usage values (if any)
emailProtection
codeSigning
anyExtendedKeyUsage
1.2.840.113583.1.1.5 (Adobe Authentic Documents Trust)
Please check your certificate accordingly and replace it if it does not fulfill this condition.

Using a Microsoft Windows Certificate Store in node.js

I am trying to create a node https server. The idea is that the server will receive the thumbprint of a certificate in the store, pull it out of the store and use it for SSL/TLS connection. I have done examples for self-signed certificates and it has worked fine.
I have the code to look through the certificate store and find the correct certificate based on its thumbprint
The code I have so far is below.
const ca = require('win-ca');
const forge = require('node-forge');
const pki = forge.pki;
const ssh = forge.ssh;
const thumbprint = process.env.THUMBPRINT;
...
for (let cert of ca.all()) {
console.log(forge.pki.certificateToPem(cert));
console.log(' ----------- \r');
const md = forge.md.sha1.create();
md.update(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes());
const hex = md.digest().toHex();
console.log(hex);
console.log('\r');
if (thumbprint === hex) {
foundCert = cert;
console.log('found\r');
break;
}
}
console.log('**********************');
console.log('\r');
//}
cnt++;
}
if (foundCert) {
console.log(forge.pki.certificateToPem(foundCert));
const pm = forge.pki.certificateToPem(foundCert);
console.log(foundCert);
}
In windows ASP.NET Core, one only needs the the certificate for https. For node.js, all the examples use a private key. I don't have the private key, but I have access to the machine's certificate store. Can I use the certificate from the store without the private key? There seems to be a piece I am missing.
Thanks

Import key programmatically to Azure Key Vault

Im able to import keys to Key Vault via PowerShell. Now I want to make a web interface to import the keys.
I tried using KeyVaultClient.ImportKeyAsync() function but Im stuck with the keyBundle parameter. I understand that keyBundle is returned from the KeyVault. I have no idea how to convert the PFX file to keyBundle.
Is there any extension method similar to the Add-AzureKeyVaultKey cmdlet, where I pass the file path and password? Or a method to convert PFX to keyBundle?
It's not quite as easy as a single method, but this should do the trick in .Net 4.6.1. It will only work for PFX that contain RSA keys, but that's essentially the only thing supported by both PFX and KeyVault. Here's the code:
X509Certificate2 cert = new X509Certificate2(
pfxBytes,
password,
X509KeyStorageFlags.Exportable);
using (RSA rsa = cert.GetRSAPrivateKey())
{
var parameters = rsa.ExportParameters(true);
KeyBundle bundle = new KeyBundle
{
Key = new JsonWebKey
{
Kty = JsonWebKeyType.Rsa,
// Private stuff
D = parameters.D,
DP = parameters.DP,
DQ = parameters.DQ,
P = parameters.P,
Q = parameters.Q,
QI = parameters.InverseQ,
// Public stuff
N = parameters.Modulus,
E = parameters.Exponent,
},
};
}
If you are using an older version of .Net, you'd have to use RSA rsa = (RSA) cert.PrivateKey instead of cert.GetRSAPrivateKey(), but the code above is recommended because it handles IDisposable and non-RSA keys more clearly.

Connect to a SharePoint site when IIS requires client certificates

I currently have an application developed in C# that helps me in managing permissions on our Share-point 2013 site. Recently, I learned we may be loosing our local instance and moving to another instance that's behind a cac enforced IIS. I have converted one of my test sites to require certificates and have tried several way to send the cert to the IIS server but I still get
"The remote server returned and error: (403) Forbidden.
Below is a few things I have tried.
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
handler.ClientCertificates.Add(pki.GetClientCertificate());
handler.UseProxy = false;
using (var client = new HttpClient(handler))
{
context connection code here
}
the pki.GetClientCertificate is a method, I made that returns a selected certificate in this case my cac cert. Its funny that SharePoint designer connects without issue or prompt. Any help on this matter would be much appreciated.
Just to add some more things I have tried
context.Credentials = new SharePointOnlineCredentials(uli.username, uli.password);
the uli username is the certificate converted to username I have a class that dose the conversion. the password is the pin converted to a secure string. I get the same message even when adding the credentials to the context.
I found a workable but slow solution here:
http://sharepoint.findincity.net/view/635399286724222582121618/ssl-certificate-error-when-using-client-object-model
The only issue with this is every time I call the context I have to send the certificate chain. One thing I changed from this users code is the following.
static void context_ExecutingWebRequest(object sender, WebRequestEventArgs e)
{
IntPtr ptr = IntPtr.Zero;
X509Certificate2 certificate = null;
X509Certificate t = null;
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Nothing to do if no cert found.
HttpWebRequest webReq = e.WebRequestExecutor.WebRequest;
//webReq.Proxy = new WebProxy("http://[ProxyAddress]");
//Specify a proxy address if you need to
// X509Certificate cert = pki.GetClientCertificate();
foreach (X509Certificate c in store.Certificates)
{
webReq.ClientCertificates.Add(c);
}
}
I just dumped all my certificates into the request because I didn't want to have a prompt every time I clicked something. So if anyone has a more efficient way to do this let me know.
The code below shows the use of the clientcontext and how it validates your cert
using (context = new ClientContext(siteurl))
{
ServicePointManager.ServerCertificateValidationCallback = delegate(object sender1, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
bool validationResult = true;
return validationResult;
};
context.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>(context_ExecutingWebRequest);
//add all your context commands below this line
}

Resources