Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically adding certificate to personal store

The project that I'm working on consists of an MVC website talking to WCF web services, authenticated by windows identity. I have a certificate for identity delegation that I'm trying to add programmatically. To do it manually I open the certificates snap-in in mmc, import the .pfx file into Personal and enter the password. I then have to click on manage private keys and allow permission to IIS_IUSRS. To replicate this process I've come up with the following console app:

class Program
{
    static void Main(string[] args)
    {
        var cert = new X509Certificate2("location.pfx", "password", X509KeyStorageFlags.MachineKeySet);
        AddCert(StoreName.My, StoreLocation.LocalMachine, cert);
        AddAccessToCertificate(cert, "IIS_IUSRS");
    }

    private static void AddCert(StoreName storeName, StoreLocation storeLocation, X509Certificate2 cert)
    {
        X509Store store = new X509Store(storeName, storeLocation);
        store.Open(OpenFlags.ReadWrite);
        store.Add(cert);
        store.Close();
    }

    private static void AddAccessToCertificate(X509Certificate2 cert, string user)
    {
        RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;

        if (rsa != null)
        {
            string keyfilepath =
                FindKeyLocation(rsa.CspKeyContainerInfo.UniqueKeyContainerName);

            FileInfo file = new FileInfo(keyfilepath + "\\" +
                rsa.CspKeyContainerInfo.UniqueKeyContainerName);

            FileSecurity fs = file.GetAccessControl();

            NTAccount account = new NTAccount(user);
            fs.AddAccessRule(new FileSystemAccessRule(account,
            FileSystemRights.FullControl, AccessControlType.Allow));

            file.SetAccessControl(fs);
        }
    }
    private static string FindKeyLocation(string keyFileName)
    {
        string text1 =
        Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
        string text2 = text1 + @"\Microsoft\Crypto\RSA\MachineKeys";
        string[] textArray1 = Directory.GetFiles(text2, keyFileName);
        if (textArray1.Length > 0)
        {
            return text2;
        }
        string text3 =
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        string text4 = text3 + @"\Microsoft\Crypto\RSA\";
        textArray1 = Directory.GetDirectories(text4);
        if (textArray1.Length > 0)
        {
            foreach (string text5 in textArray1)
            {
                textArray1 = Directory.GetFiles(text5, keyFileName);
                if (textArray1.Length != 0)
                {
                    return text5;
                }
            }
        }
        return "Private key exists but is not accessible";
    }
}

Unfortunately this gives the error:

The address of the security token issuer is not specified. An explicit issuer address must be specified in the binding for target 'https://service.svc' or the local issuer address must be configured in the credentials.

I recognise that I have a large knowledge gap with this stuff so I'd appreciate some guidance!

My question is, what's the difference between my manual and automated process?

like image 671
NickL Avatar asked Sep 13 '25 20:09

NickL


1 Answers

This line:

var cert = new X509Certificate2("location.pfx", "password", X509KeyStorageFlags.MachineKeySet);

should have been

var cert = new X509Certificate2("location.pfx", "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);

It was X509KeyStorageFlags.PersistKeySet that was missing.

I got some helpful information on certificates from here.

like image 176
NickL Avatar answered Sep 15 '25 11:09

NickL