adding extensions to a certificate request ( password-challenge ) with C# and CertENrollLib

2.7k views Asked by At

I have to add extensions to a certificate request ( CSR ) in such a way that I respect a given structure. Namely this oneASN .1 structure

On the left is the structure I must respect for the chalenge password, on the right the structure I get when I simply generate a OID object from the challenge-password OID value, then embedding all this directly into the extension list of the PKCS10 request:

CObjectId cp_oid = new CObjectId();

// OID 1.2.840.113549.1.9.7
// cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
cp_oid.InitializeFromValue("1.2.840.113549.1.9.7");

then I create a CX509Extension object add the OID to the PKCS10 request:

CX509Extension extension = new CX509Extension();
string b64__challengePassword=System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(this.challengePassword));

extension.Initialize(cp_oid, EncodingType.XCN_CRYPT_STRING_BASE64_ANY, b64__challengePassword);
_certificateRequest.X509Extensions.Add(extension);

since the structure is clearly different from what I must obtain ( see the right part of the previous picture ) , I am now using a more sophisticated approach:

_certificateRequest = new CX509CertificateRequestPkcs10();
_certificateRequest.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, (CX509PrivateKey)_privateKey, null);
_certificateRequest.Subject = (CX500DistinguishedName)_subjectName;

CObjectIds cp_oids = new CObjectIds();

CObjectId cp_oid = new CObjectId();
// OID 1.2.840.113549.1.9.7
// cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
cp_oid.InitializeFromValue("1.2.840.113549.1.9.7");

CX509Extension _extension = new CX509Extension();

cp_oids.Add(cp_oid);

//now how do I add that oid list to the 1.2.840.113549.1.9.14 OID ?
//I try with CX509ExtensionEnhancedKeyUsage instead of a simple CX509Extension
//which one of all these is the correct extensions?
/*
 *  IX509ExtensionAlternativeNames          Specifies one or more alternative name forms for the subject of a certificate.
    IX509ExtensionAuthorityKeyIdentifier    Represents an AuthorityKeyIdentifier extension.
    IX509ExtensionBasicConstraints          Specifies whether the certificate subject is a certification authority and, if so, the depth of the subordinate certification authority chain.
    IX509ExtensionCertificatePolicies        Represents a collection of policy information terms.
    IX509ExtensionMSApplicationPolicies     Represents a collection of object identifiers that indicate how a certificate can be used by an application.
    IX509ExtensionEnhancedKeyUsage            Represents a collection of object identifiers that identify the intended uses of the public key contained in a certificate.
    IX509ExtensionKeyUsage                    Represents restrictions on the operations that can be performed by the public key contained in the certificate.
    IX509Extensions                         Manages a collection of IX509Extension objects.
    IX509ExtensionSmimeCapabilities            Represents a collection that reports the decryption capabilities of an email recipient to an email sender.
    IX509ExtensionSubjectKeyIdentifier        Represents a SubjectKeyIdentifier extension used to identify a signing certificate.
    IX509ExtensionTemplate                    Represents a CertificateTemplate extension that contains a version 2 template.
    IX509ExtensionTemplateName                Represents a CertificateTemplateName extension that contains a version 1 template.
                 */

CX509ExtensionEnhancedKeyUsage eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(cp_oids);
eku.Critical = false;

CX509AttributeExtensions InitExt = new CX509AttributeExtensions();

//  Add the extension objects into an IX509Extensions collection.
CX509Extensions ext1 = new CX509Extensions();
ext1.Add((CX509Extension)eku);
//  Use the IX509Extensions collection//to initialize an IX509AttributeExtensions object.
CX509AttributeExtensions ext1att = new CX509AttributeExtensions();
ext1att.InitializeEncode(ext1);

//Add the IX509AttributeExtensions object to an IX509Attributes collection.
CX509Attributes att1 = new CX509Attributes();
att1.Add((CX509Attribute)ext1att);

//Use the IX509Attributes collection to initialize an ICryptAttribute object.
CCryptAttribute crypt1 = new CCryptAttribute();
crypt1.InitializeFromValues(att1);

//Initialize a CMC or PKCS #10 request object and retrieve the ICryptAttributes collection.


//Add the ICryptAttribute object to the ICryptAttributes collection for the request.
_certificateRequest.CryptAttributes.Add(crypt1);

//Console.WriteLine("-- encode");
this.status2 = this.status2 + "-- encode <BR>";


try
{
    _certificateRequest.Encode();
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

string rawData = _certificateRequest.get_RawData();

Console.WriteLine("data=" + rawData);

However I get the puzzling error "The file exists. (Exception from HRESULT: 0x80070050)" at the end of the process when encoding the request , I tried with different smartcards ad the key containers are OK, not full.

Is my approach toward adding this challenge-password extension correct and how can I interpret this error?

2

There are 2 answers

0
Thanh Nguyen On

Below is code to include Challenge Password into PKCS10 generated by CertEnroll:

private static byte[] getDerBytes(int tag, byte[] data)
    {
        if (data.Length > byte.MaxValue)
        {
            throw new NotSupportedException("Support for integers greater than 255 not yet implemented.");
        }

        var header = new byte[] { (byte)tag, (byte)data.Length };
        return header.Concat(data).ToArray();
    }

and

public static byte[] EncodePrintableString(string data)
    {
        var dataBytes = Encoding.ASCII.GetBytes(data);

        return getDerBytes(0x13, dataBytes);
    }

and finnally:

CObjectId cp_oid = new CObjectId();

                cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
                byte[] b64__challengePassword = EncodePrintableString("password");

                ICryptAttribute ChallengeAttributes = new CCryptAttribute();
                ChallengeAttributes.InitializeFromObjectId(cp_oid);

                CX509Attribute ChallengeAttribute = new CX509Attribute();
                ChallengeAttribute.Initialize(cp_oid, EncodingType.XCN_CRYPT_STRING_BASE64_ANY, 
                                                    Convert.ToBase64String(b64__challengePassword));
                ChallengeAttributes.Values.Add(ChallengeAttribute);

                objPkcs10.CryptAttributes.Add((CCryptAttribute)ChallengeAttributes);
0
Mike On

The answer to the error you are getting "The file exists. (Exception from HRESULT: 0x80070050)" is because trying to set a key on a template that already has a key. just comment this:

    CX509ExtensionEnhancedKeyUsage eku = new CX509ExtensionEnhancedKeyUsage();
    eku.InitializeEncode(cp_oids);
    eku.Critical = false;

    CX509AttributeExtensions InitExt = new CX509AttributeExtensions();


  //  Add the extension objects into an IX509Extensions collection.
    CX509Extensions ext1= new CX509Extensions();
    ext1.Add((CX509Extension)eku);

and it should work.


search for this in the in the article Working with Active Directory Certificate Service via C# for:

Seems that we finished, but if we just execute it will throw an exception to us, said that the file exists when adding some extensions.

it explains everything.


from the article:

The exception message could be a little bit confusing. In fact this is because we defined something which had been defined in the certificate template. If we dig into the source code we can see that the exception occurred when we added the key usage extension.

And if we get back to the CA server and open the template we are using, we can find that the key usage had been defined in the template. This means in the code, or in the certificate request we should not specify it again.

Hence we need to comment the code for adding the key usage, also we need to comment the enhanced key usage part since it had been defined in the template, too. Because we let the request supply the subject name so here we can still specify the subject information in the request. The method for generating request message would be like this.