Digitally sign a Visual Studio 2012 VSIX extension - c#

I am trying to sign a Visual Studio 2012 extension that is packaged as a VSIX file.
I have followed the instructions at http://www.jeff.wilcox.name/2010/03/vsixcodesigning/; however, I am interested in performing signing without specifying a pfx file and password.
For example, if I were to call 'signtool.exe', my command line would be:
"signtool.exe" sign /n MySubjectName /t 'http://timestamp.verisign.com/scripts/timstamp.dll' /d "MyDescription" MyPackage.vsix
I understand that this command does not work with VSIX files, though it does work for an MSI archive.
With this command, I do not need to specify a password or pfx file when calling signtool. The best installed certificate is selected, using the specified subject MySubjectName.
Following the code on Jeff's Blog, the signing step requires pfx file name and password to be defined to create the X509Certificate2 used in signing:
private static void SignAllParts(Package package, string pfx, string password, string timestamp){
var signatureManager = new PackageDigitalSignatureManager(package);
signatureManager.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
/*...*/
signatureManager.Sign(toSign, new System.Security.Cryptography.X509Certificates.X509Certificate2(pfx, password));
}
Is there any API involving PackageDigitalSignatureManager that might let me find a X509Certificate based on MySubjectName so that I can sign against that?

I've solved this by iterating over the certificates found in the current user's store. I filter by the issuer name and take only valid certificates, then I loop over the matching certificates and return the first one which matches also the subject name:
public static X509Certificate2 Find(string issuer, string subject)
{
var certStore = new X509Store (StoreName.My, StoreLocation.CurrentUser);
certStore.Open (OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find (X509FindType.FindByIssuerName, issuer, true);
foreach (var cert in certCollection)
{
if (cert.FriendlyName == subject)
{
return cert;
}
}
return null;
}

Related

Cant access certificates in X509Store from asp.net core 2.1 with thumbprints

I have strange problem when accessing X509Store from IIS. I can't look them up.
If I access both the rp cert and ca cert from powershell both are there,
dir cert: -Recurse | Where-Object { $_.Thumbprint -like "thumprintstring" }
I have checked that the thumbprints don't have a hidden char in the beginning of thumbprint
I have set that the certificates are exportable when I install them
I have for the moment set it accessable for everyone(its a certificate to a test server) in certficate
store
This is code I use
StoreLocation location = certificateConfig.UseCurrentUserStoreLocation ? StoreLocation.CurrentUser : StoreLocation.LocalMachine;
using (var clientCertStore = new X509Store(StoreName.My, location))
{
clientCertStore.Open(OpenFlags.ReadOnly);
//Search for the client cert
X509Certificate2 rpCert = GetCertByThumbprint(clientCertStore, certificateConfig.RpCertThumbprint);
if (rpCert == null)
{
throw new InvalidOperationException("No rp cert found for specified thumbprint #" + certificateConfig.RpCertThumbprint +"# "+location);
}
ClientCertificates.Add(rpCert);
}
<snip>
private X509Certificate2 GetCertByThumbprint(X509Store certStore, string thumbprint)
{
var certs = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
return certs.Count > 0 ? certs[0] : null;
}
The rpcert is always null whatever i try.
Do I need another way to open up the store from IIS?
Any ideas or suggestions? What am I missing?
The problem was not what I expected. The config read from enviromentvariables that had been deleted so they didnt show in enviromentvariables and the server had not been restarted. And the deleted ones had most likely the bad character infront of the thumbprint.
Restarting iis doesn't solve this since the network service account doesnt reread these when already loggedon.
Follow up question: Is possible to relogin in network service account without restarting the server?

Azure, App-service, create X509Certificate2 object from string

Having an App-service in Azure, and working on the AzureServiceManagementAPI, I was downloading the file that contains the managememnt certificate for each subscription.
Any how using the certificate string from the file I'm trying to create a X509Certificate2 object.
string cerStr = subscription.Attribute("ManagementCertificate").Value;
X509Certificate2 x509 = new X509Certificate2(Convert.FromBase64String(cerStr), string.Empty, X509KeyStorageFlags.MachineKeySet)
The constructor of X509Certificate2 throw an exception
Access denied.
System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32
hr) at
System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[]
rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet,
SafeCertContextHandle& pCertCtx) at
System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[]
rawData, Object password, X509KeyStorageFlags keyStorageFlags)
Since no one has answered this questions, I will try and have go at it. Please correct me if I am wrong, but the problem I think is the following line of code:
new X509Certificate2(Convert.FromBase64String(cerStr), string.Empty, X509KeyStorageFlags.MachineKeySet)
This line of code will try to add a new certificate to the certificate store of the virtual machine. All certificates used by the runtime, needs to be hosted in a store somewhere. This is not a good idea because the certificate store of the virtual machine hosting the app service is nothing that you should be storing anything in, it's part of the infrastructure which is not of your concern when you are working with app services.
What you need to do is to upload the certificate through the azure portal instead (if they are not already there). I ended up reusing a SSL certificate already in place for this purpose. When this is done, you can retreive that certificate in code. You will need to add a new App Setting under "Application Settings" key in the Azure portal for your app service, named WEBSITE_LOAD_CERTIFICATES. The value should be the thumbprint of the certificate.
To retrieve the cert, you should do something like this:
public async Task<X509Certificate2> GetCertificate(string certificateThumbprint)
{
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.OfType<X509Certificate2>()
.FirstOrDefault(x => x.Thumbprint == certificateThumbprint);
store.Close();
return cert;
}
You might be able to get thumbprint of the cert by navigating your subscription using the azure resource explorer https://resources.azure.com/
As Fredrik mentioned the issue is due to the code
X509Certificate2 x509 = new X509Certificate2(Convert.FromBase64String(cerStr), string.Empty, X509KeyStorageFlags.MachineKeySet)
In the Azure WebApp, if we try to use the certificate, we need to upload the certificate from the Azure portal. Add the WEBSITE_LOAD_CERTIFICATES with thumbprint value in the Azure WebApp application. More detail info please refer to blog.
Web application to access the certificate, snippet code from the blog
static void Main(string[] args)
{
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
// Replace below with your cert's thumbprint
“E661583E8FABEF4C0BEF694CBC41C28FB81CD870”,
false);
// Get the first cert with the thumbprint
if (certCollection.Count > 0)
{
X509Certificate2 cert = certCollection[0];
// Use certificate
Console.WriteLine(cert.FriendlyName);
}
certStore.Close();
}

Trouble authenticating with SshUserKeyCredentials in libgit2sharp-SSH

Having some trouble authenticating with SshUserKeyCredentials using libgit2sharp-SSH:
var co = new CloneOptions();
co.CredentialsProvider = (_url, _user, _cred) => new SshUserKeyCredentials { PrivateKey="C:\\path\\to\\private_key" };
Repository.Clone("git#... .repository.git", path, co);
I found the SshUserKeyCredentials object browsing through the source code so my first question would be if it is possible to use this object to do deploy key based checkout from gitlab?
The object seems to want any combination of PrivateKey, Username, PublicKey and Passphrase. I'm currently using a PrivateKey.
The error I end up with:
{"Failed to start SSH session: Unable to exchange encryption keys"}
If this way isn't supposed to work is there an alternative way of using deploy keys to programmatically manage git from an C# environment?
I was able to figure out the following through trial-and-error and scouring the web.
You cannot have null for any field; use string.Empty.
Public and private key must be provided and in the proper format.
Private key had to be in PEM format (either use PuttyGen Conversion menu => Export OpenSSH or use openssl rsa -in id_rsa -out id_rsa.pem).
Public key had to be single line, starting with type, followed by base64 key, no comment at the end (this is the format shown in the public key text box on PuttyGen, except you have to remove the comment), e.g.
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAoblahblahblahblahblah
I added username git because that's what Bitbucket requires. Not sure you need that, but it can't be null, do string.Empty.
Example:
CredentialsHandler handler = (_url, _user, _cred) => new SshUserKeyCredentials
{
PrivateKey = #"C:\Users\blah\.ssh\keys\bitbucket.pem",
Username = "git",
Passphrase = string.Empty,
PublicKey = #"C:\Users\blah\.ssh\keys\bitbucket.pub"
}
See also:
PHP ssh2_auth_pubkey_file(): Authentication failed using public key: Invalid key data, not base64 encoded

Reading all certificates from asp.net

I have a problem with reading certificates. I have a web service that has to get a certificate serial number using part of the subject. Everything works fine if I'm doing it from a form but when I try it from a web service it seems that it cannot find any certificate. I'm using this code to read all of the the certificates:
X509Store store = new X509Store();
store.Open(OpenFlags.ReadOnly);
if (args.Parameters["CertificateName"].ToString() != "")
{
foreach (X509Certificate2 mCert in store.Certificates)
{
if (mCert.Subject.Contains("OU=" + args.Parameters["CertificateName"].ToString()))
{
SerialNum = mCert.SerialNumber;
break;
}
}
if (SerialNum == String.Empty)
{
throw new Exception("Certificate not found with name: " + args.Parameters["CertificateName"].ToString() + " ;" + " OU=" + args.Parameters["CertificateName"]);
}
}
else
{
foreach (X509Certificate2 mCert in store.Certificates)
{
if (mCert.Subject.Contains("OU=Eua"))
{
SerialNum = mCert.SerialNumber;
break;
}
}
if (SerialNum == String.Empty)
{
throw new Exception("Haven't found default certificate ;");
}
}
store=null;
You are using the parameterless constructor for X509Store which according to the documentation will open the cert store for the current user. Well, the current user for your forms application is probably not the same as the current user for your web application, which most likely runs within an AppDomain configured to use a service account. So that means the web application won't be able to find it.
To fix this, you have two options
Option 1
First store your certificate in the machine store (not the user store). Then, in your code, open the store using a different constructor that lets you specify the store location, and specify that you want the machine store. Like this:
var store = new X509Store(StoreLocation.MachineStore);
Option 2
Maintain two copies of the certificate. Follow these steps:
Export the certificate from your current user's cert store
Start certificate manager using "RunAs" to impersonate the service account for the app domain, e.g. runas /user:MyDomain\MyServiceAccount "cmd /c start /B certmgr.msc". When prompted make sure you tell it you want to work with the current user's cert store, not the machine store.
Import the certificate there
Open the cert up and make sure its chain of trust is intact; if any intermediate or root certs are missing, you may have to import those as well.
Remember when this cert expires, you will have to replace both copies.

how to load password protected certificates from the X509Store?

I am building an ACS protected Azure WCF service that will require clients to authenticate via a certificate.
I would like the client (and the server) to load their respective password certs from the X509Store instead of from the file system.
I am using this code:
private static X509Certificate2 GetCertificate(string thumbprint)
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbprint, false);
certStore.Close();
if (certCollection.Count == 0)
{
throw new System.Security.SecurityException(string.Format(CultureInfo.InvariantCulture, "No certificate was found for thumbprint {0}", thumbprint));
}
return certCollection[0];
}
Problem is, it's not loading the private key which it needs for authentication. I have tried to modify the return statement to this:
return new X509Certificate2(certCollection[0].Export(X509ContentType.Pfx, "password"));
However, this fails with a CryptographicException "The spcecified network password is incorrect".
Edit:
The .Export() method works properly if you don't pass the password argument in.
Any help on this?
When you export, the password you provide is the password you want to use for the exported file, it's not the password for the source certificate.
I'm not sure what you can do with X509Store and password-protected certs because the password should be supplied to the X509Certificate constructor and you get already-instantiated objects out of the store.
I think you can just get the raw data from the cert you want and construct a new one with the password you want. For example:
X509Certificate2 cert = new X509Certificate2(certCollection[0].GetRawCertData, password);
I would also suggest you try to use SecureString when dealing with passwords (but that's a different bag of worms...)
I used the Export without the 'password' parameter and it worked without issue.
When the cert was imported into the certificate store, I think the key has to be marked as "exportable" otherwise I don't think you can export the private key..

Categories