As the title says, I need to programmatically copy a cert (given the thumbprint) from the LocalComputer store to the CurrentUser store. I've been digging around in the X509Certificate2 definitions and trying stuff, but nothing seems to be working. Here is what I have so far
certPath = "#"C:\%temp%\Cert.pfx";
certPass = "CertPassHere";
X509Store localMachineStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
localMachineStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificate = localMachineStore.Certificates.Find(X509FindType.FindByThumbprint, "certThumbprint", true);
byte[] rawCertData = certificate[0].Export(X509ContentType.Pfx, certPass);
File.WriteAllBytes(certPath, rawCertData);
localMachineStore.Close();
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(certPath, certPass, X509KeyStorageFlags.PersistKeySet);
X509Store currentUserStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
foreach (X509Certificate2 cert in collection)
{
Console.WriteLine("Subject is: '{0}'", cert.Subject);
Console.WriteLine("Issuer is: '{0}'", cert.Issuer);
currentUserStore.Add(cert);
}
currentUserStore.Close();
File.Delete(certPath);
I feel like I'm somewhat on the right track here, but any help is very much appreciated :)
Saving it to a file is unnecessary, since you can load the PFX from the byte[] directly.
You don't seem to have a call to currentUserStore.Open(OpenFlags.ReadWrite), is the code finishing, or throwing a CryptographicException?
You need to import the PFX with X509KeyStorageFlags.UserKeySet because the private key will "remember" that it was exported from the machine keystore, and it wants to go back there.
Passing true as the third argument to X509Certificate2Collection.Find is usually not what you want, it's mostly only handy when searching by subject, to ignore expired things.
.
string thumbprint = IAssumeYouHaveThisForReal();
X509Certificate2Collection certificates = localMachineStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbprint,
false);
byte[] tempPfx = certificates[0].Export(X509ContentType.Pfx, "hi");
X509Certificate2 copyWithUserKey = new X509Certificate2(
tempPfx,
"hi",
X509KeyStorageFlags.UserKeySet /*| X509KeyStorageFlags.Exportable if you like */);
X509Store currentUserMy = new X509Store(StoreName.My, StoreLocation.CurrentUser);
currentUserMy.Open(OpenFlags.ReadWrite);
currentUserStore.Add(copyWithUserKey);
certificates = currentUserStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbprint,
false);
if (certificates.Count != 1)
throw new InvalidOperationException();
Related
Both the cert and the key file are of type string.
I tried using this:
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
rsaKey.ImportParameters(keyfile);
X509Certificate2 cert = new X509Certificate2(certfile);
cert.PrivateKey = rsaKey;
cert.Export(X509ContentType.Pkcs12, "xyz");
RestAsynchronicClient client = new RestAsynchronicClient(url, RestDataStandard.JSON, null, cert, logger);
Here I am getting conversion error in the second line. Conversion error from string to RSAParameter.
There's no automatic conversion from a file path, or file contents, to an RSAParameters; the RSACryptoServiceProvider is out of date and not recommended for new code, and the PrivateKey property on certificates is fully [Obsolete] in new versions of .NET.
With .NET 5+, this is easy:
byte[] pfxBytes;
using (X509Certificate2 cert = X509Certificate2.CreateFromPemFile(certFile, keyFile))
{
pfxBytes = cert.Export(X509ContentType.Pkcs12, pfxPwd);
}
Or, in the style closer to the code you've written:
byte[] pfxBytes;
using (X509Certificate2 cert = new X509Certificate2(certFile))
using (RSA key = RSA.Create())
{
key.ImportFromPem(File.ReadAllText(keyFile));
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(key))
{
pfxBytes = certWithKey.Export(X509ContentType.Pkcs12, pfxPwd);
}
}
Your reference snippet then goes on to ignore the PFX/PKCS12 output and pass the cert to RestAsynchronicClient. Because of some idiosyncrasies on Windows, that generally won't work if you load the cert from this style. But, if you load the PFX into a new X509Certificate2 object, that'll be in a slightly different state and everything'll be happy.
RestAsynchronicClient client = new RestAsynchronicClient(
url,
RestDataStandard.JSON,
null,
new X509Certificate2(pfxBytes, pfxPwd),
logger);
I've encountered a problem when trying to implement digital signature with iText7. According to the documentation of iText and couple examples I need to implement IExternalSignature (here), like so: IExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm); but this is where I get the exception:
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert.PrivateKey).Private;
which basically means "Invalid key to use in the current state"(?).
Most of the examples are from older version of iText library and Java (I'm using C#) and I cant quite figute it out.
I'll be very grateful for any tips. Thanks!
EDIT:
Here's some example code just to replicate the exception:
static void Main(string[] args)
{
string output = "D:/Development/TestApp/testOutputMoje.pdf";
string input = "D:/Development/TestApp/testInput.pdf";
PdfReader reader = new PdfReader(input);
string digestAlgorithm = DigestAlgorithms.SHA256;
List<X509Certificate2> oCertChain = new List<X509Certificate2>();
//getting certificates from store
X509Store store = new X509Store(StoreName.My);
store.Open(OpenFlags.ReadOnly);
foreach (var oCert in store.Certificates)
{
oCertChain.Add(oCert);
}
store.Close();
//siginig with first certificate - just example
var cert = oCertChain[0];
//exception here:
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert.PrivateKey).Private;
X509Certificate[] bouncyCert = { Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(cert) };
StampingProperties stampProp = new StampingProperties();
stampProp.PreserveEncryption();
IExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm);
PdfSigner signer = new PdfSigner(reader, new FileStream(output, FileMode.Create), stampProp);
signer.SignDetached(signature, bouncyCert, null, null, null, 0, CryptoStandard.CADES);
reader.Close();
}
Couple more informations about the certificate (I cant show You any details unfortunately)
problem is in certificate. It has to be marked as "Exportable". I don't know how solve this case, but in my case I used own certificate this way:
var cert = new X509Certificate2();
cert.Import(File.ReadAllBytes(#"C:\temp\certificate.cer"), "password", X509KeyStorageFlags.Exportable);
This works.
You need to find the way to change your certificate flag. Maybe something with store.Open(OpenFlags.MaxAllowed), but this doesn't work.
My project consists in signing a PDF file using PKS11 (usb smartcard).
What I need is:
1) Plug in the USB smart card (and let the OS load the certificate in CurrentUser store (Windows))
2) Call an application , no GUI needed, that sign a determined PDF with the loaded cert.
Step 2 would need the user to input the PIN of the smartcard. For now that is not a concern, I can statically set it for testing purposes.
The posts I have been reading are:
checking-for-the-accessibility-of-smart-card-private-keys-in-windows-10
find-certificate-on-smartcard-currently-on-reader
load-a-smart-card-or-other-private-certificate-in-cryptoserviceprovider-for-sign
how-do-i-sign-a-pdf-document-using-a-certificate-from-the-windows-cert-store
sign-pdf-with-itextsharp-5-3-3-and-usb-token
// Set up the PDF IO
PdfReader reader = new PdfReader(#"C:\Users\martin\Documents\tosign.pdf");
PdfStamper stamper = PdfStamper.CreateSignature(reader,
new FileStream(#"C:\Users\martin\Documents\SignedPdf.pdf", FileMode.Create),
'\0');
PdfSignatureAppearance sap = stamper.SignatureAppearance;
sap.Reason = "For no apparent reason";
sap.Location = "Place";
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2 cert = fcollection[0];
BcX509.X509CertificateParser cp = new BcX509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
cp.ReadCertificate(cert.RawData)
};
Console.WriteLine(cert);
X509Certificate2 signatureCert = new X509Certificate2(cert);
IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");
MakeSignature.SignDetached(sap, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
I get a runtime error
System.ArgumentException: 'Unknown encryption algorithm
System.Security.Cryptography.RSACng'
in the line
IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");
I read somewhere that externalSignature is to be implemented with custom code depending on the USB vendor. Though in all the posts mentioned here, it seems that line should work with no problems. I tried to change the algorithm to SHA-256. Also I have been digging Github code of IExternalSignature interface to try to get a grasp out of it.
In theory, I know the private key of the USB device will never be accessible, therefore I can not just try to sign with a "getprivatekey" method kind.
In practice, I am a MEAN stack and Python dev, I have never coded in C#.
I am setting up AS2 communication for signing files to a customer. As I understand signing to work through AS2 communication, we will sign the data we send with our private key, and they will verify it with our public key.
My problem:
My IT department has given me a .cer and a .key file. The .cer file has no private key in it, and obviously the .key file is the private key. The customer will add the .cer file to their trusted root to verify our messages. I am having trouble understanding how to sign my data with the .key file. It's not something I can add to a Personal certificate store, so I can't simply get the certificate and do this:
//Open my Personal cert store.
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains("My certificate subject"))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
// Hash the data
SHA256Managed sha256 = new SHA256Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] hash = sha256.ComputeHash(arMessage);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));
How do I get my private key as a RSACryptoServiceProvider directly using the .key file, since a .key file can't be stored in the certificate store?
I know only how to proceed with PFX files, but converting your KEY file to PFX should not be a problem. See this post:
Is it possible to convert an SSL certificate from a .key file to a .pfx?
Later on, you can manually open the certificate file using this method:
// This you should know
var certPath = #"path-to-file.pfx";
var certPass = #"password-goes-here";
// Create a collection object and populate it using the PFX file
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(certPath, certPass, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
//Then you can iterate over the collection:
foreach (X509Certificate2 cert in collection)
{
// Bingo
// Here you can do whatever you want with "cert"
}
I've done similar things in a slightly different way. Forgive me if this is not helpful at all and I can delete it.
I'm not doing any signing here but you might be able to figure out how from this.
var collection = new X509Certificate2Collection();
collection.Import(System.Web.Hosting.HostingEnvironment.MapPath(pathToPrivateKey), privateKeyPassword, X509KeyStorageFlags.MachineKeySet);
var requestToPing = (HttpWebRequest)WebRequest.Create(dropOffURL);
requestToPing.Method = "POST";
requestToPing.PreAuthenticate = true;
requestToPing.ClientCertificates.Add(collection[0]);
I've build my own root CA certificate with Bouncy Castle, and I'm using it to build other certificates. I want to build a Certificate Revocation List (CRL) to include the list of revoqued certificates, using Bouncy Castle C#. Example:
//Retrieve CA root certificate
X509Store CAstore = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
CAstore.Open(OpenFlags.ReadWrite | OpenFlags.OpenExistingOnly);
X509Certificate2Collection x509Certificate2Collection =
CAstore.Certificates.Find(X509FindType.FindBySerialNumber,
this.textBoxSerialCA.Text, true);
X509Certificate2 cert = x509Certificate2Collection[0];
var certCA = DotNetUtilities.FromX509Certificate(cert);
CAstore.Close();
X509V2CrlGenerator crlGen = new X509V2CrlGenerator();
crlGen.SetIssuerDN(certCA.IssuerDN);
crlGen.SetThisUpdate(DateTime.Now);
crlGen.SetNextUpdate(DateTime.Now.AddYears(1));
crlGen.SetSignatureAlgorithm("SHA1withRSA");
crlGen.AddCrlEntry(BigInteger.One, DateTime.Now, CrlReason.PrivilegeWithdrawn);
crlGen.AddExtension(X509Extensions.AuthorityKeyIdentifier,
false,
new AuthorityKeyIdentifierStructure(certCA));
crlGen.AddExtension(X509Extensions.CrlNumber,
false,
new CrlNumber(BigInteger.One));
var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
var Akp = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert.PrivateKey).Private;
X509Crl crlTemp = crlGen.Generate(Akp,random);
All is OK until this point. How can I save the X509Crl object into a .crl file?
Best regards.
This answer comes quite late, but you can use the PemWriter class in Bouncy Castle to write to a PEM file.
PemWriter pemWriter = new PemWriter(new StreamWriter(File.Open(fileName, FileMode.Create)));
pemWriter.WriteObject(crlTemp);
pemWriter.Writer.Flush();
pemWriter.Writer.Close();
In BouncyCastle.Crypto version 1.7.4114.6375, I was able to take your code and simply add:
var b = crlTemp.GetEncoded();
System.IO.File.WriteAllBytes(#"C:\temp\test.crl", b);
Then, in Windows, double clicking on the 'test.crl' file will open the standard, built-in Certificate Revocation List dialog without any errors and all the information looks correct when compared to other CRL files.
And after you've got a CRL in PEM format you can convert it via openssl with the following command:
openssl crl -in list.pem -outform der -out list.crl