Arsen Zahray
Arsen Zahray

Reputation: 25327

How to use BouncyCastle to generate X509Certificate2 which can be used for WCF authentication?

Here's my WCF service code:

        ServiceHost svh = new ServiceHost(typeof(MyClass));

        var tcpbinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential, true);
        //security

        tcpbinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
        svh.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new BWUserNamePasswordValidator();
        svh.Credentials.UserNameAuthentication.UserNamePasswordValidationMode =UserNamePasswordValidationMode.Custom;
        svh.Credentials.ServiceCertificate.Certificate = GenerateCertificate(myCert);

        svh.AddServiceEndpoint(typeof(IMyClass), tcpbinding, location);            

        svh.Open();

And here's the code, which I'm using to generate the certificate:

    static X509Certificate2 GenerateCertificate(string certName)
    {
        var keypairgen = new RsaKeyPairGenerator();
        keypairgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));

        var keypair = keypairgen.GenerateKeyPair();

        var gen = new X509V3CertificateGenerator();

        var CN = new X509Name("CN=" + certName);
        var SN = BigInteger.ProbablePrime(120, new Random());

        gen.SetSerialNumber(SN);
        gen.SetSubjectDN(CN);
        gen.SetIssuerDN(CN);
        gen.SetNotAfter(DateTime.MaxValue);
        gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
        gen.SetSignatureAlgorithm("MD5WithRSA");
        gen.SetPublicKey(keypair.Public);

        gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
                    new SubjectKeyIdentifierStructure(keypair.Public));        

        var newCert = gen.Generate(keypair.Private);

        return new X509Certificate2(DotNetUtilities.ToX509Certificate((Org.BouncyCastle.X509.X509Certificate)newCert));
    }

When I'm starting the server it crashes with following exception:

ArgumentException: It is likely that certificate 'CN=MyCert' may not 
have a private key that is capable of key exchange or the process may not have 
access rights for the private key. Please see inner exception for detail.

The inner exception is null.

What did I do wrong?

Upvotes: 1

Views: 2811

Answers (1)

Michael Edenfield
Michael Edenfield

Reputation: 28338

Key pairs used for certificate exchange need to be created explicitly for that purpose. I think you just need to add another extension to your certificate generator like this:

gen.AddExtension(
    X509Extensions.KeyUsage, 
    true,
    new KeyUsage(KeyUsage.keyCertSign));

Not sure if that syntax is exactly correct but that's the idea: you need to make the key in the certificate as being for certificate signing, and that is a "critical" extension if present.

Upvotes: 2

Related Questions