Search code examples
c#bouncycastlex509certificate2certutil

"Missing stored keyset" when using mix of bouncycastle and .NET


After successfully attaching a ECC private key to a X509Certificate2 that I retrieved from our PKI (following mostely the answer to this SO question), I have the requirement to do this without P/Invoke.

So I try to attach the key using bouncycastle like this:

var pkcs12Store = new Pkcs12Store();
var certEntry = new X509CertificateEntry(bouncyCastleCertificate);
pkcs12Store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(bouncyCastleKeyPair.Private), new[] { certEntry });
using (MemoryStream pfxStream = new MemoryStream())
{
    pkcs12Store.Save(pfxStream, null, new SecureRandom());
    pfxStream.Seek(0, SeekOrigin.Begin);
    byte[] rawData = pfxStream.ToArray();
    var result = Pkcs12Utilities.ConvertToDefiniteLength(rawData);
    var microsoftCert = new X509Certificate2();
    microsoftCert.Import(result, (string)null, X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
    return microsoftCert ;
}

This seems to work partly because I can see a new certificate in the MMC telling me that the certificate is vaild and that I have a private key for this certificate. There also is a new entry in %USER_HOME%\AppData\Roaming\Microsoft\SystemCertificates\My\Keys.

But when I use certutil -user -store my then it tells me "missing stored keyset". So the stored certificate seems to miss the link to the stored key or has a wrong link.

I think it is just a little detail missing but I just cannot find out what it is. Any ideas?

By the way I already tried to use a password for key and container as suggested in some topics I found on the net, that didn't help either.


Solution

  • OK, figured it out myself. The culprit is not the code above but the way the keypair was generated. For everybody who stumbles over the same issue here is what was wrong and what does work.

    Original generation of keys that caused the problem:

    var eccParameters = TeleTrusTNamedCurves.GetByName("brainpoolP384r1");
    var domainParameters = new ECDomainParameters(eccParameters.Curve, eccParameters.G, eccParameters.N, eccParameters.H, eccParameters.GetSeed());
    IAsymmetricCipherKeyPairGenerator keyPairGenerator = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
    keyPairGenerator.Init(new ECKeyGenerationParameters(domainParameters, new SecureRandom()));
    AsymmetricCipherKeyPair keyPair = keyPairGenerator.GenerateKeyPair();
    

    And that's the working code:

    IAsymmetricCipherKeyPairGenerator keyPairGenerator = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
    DerObjectIdentifier curveIdentifier = TeleTrusTObjectIdentifiers.BrainpoolP384R1;
    keyPairGenerator.Init(new ECKeyGenerationParameters(curveIdentifier, new SecureRandom()));
    return keyPairGenerator.GenerateKeyPair();
    

    Hey, Bouncycastle you do a great job with your code, but the documentation really sucks. ;-)