1

I am trying to create certificate request programmatically from existing public key. But I get an "The requested property value is empty. (Exception from HRESULT: 0x80094004)" exception. Here is my code:

    private static string CreateCertRequestMessage(string encodedPublicKeyInfo)
    {
        CObjectId objAlg = new CObjectId();
        objAlg.InitializeFromAlgorithmName(
            ObjectIdGroupId.XCN_CRYPT_PUBKEY_ALG_OID_GROUP_ID, 
            ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 
            AlgorithmFlags.AlgorithmFlagsNone, 
            "RSA");

        CX509PublicKey objPublicKey = new CX509PublicKey();
        objPublicKey.Initialize(objAlg, encodedPublicKeyInfo, "", EncodingType.XCN_CRYPT_STRING_HEX);
        Console.WriteLine(objPublicKey.Algorithm.FriendlyName);
        Console.WriteLine(objPublicKey.Algorithm.Value);
        Console.WriteLine(objPublicKey.Length);
        Console.WriteLine(objPublicKey.EncodedKey);

        var objPkcs10 = new CX509CertificateRequestCertificate();

        objPkcs10.InitializeFromPublicKey(
            X509CertificateEnrollmentContext.ContextUser,
            objPublicKey,
            string.Empty);


        var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
        objExtensionKeyUsage.InitializeEncode(
            CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
            CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
            CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
            CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE);
        objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);

        var objObjectId = new CObjectId();
        var objObjectIds = new CObjectIds();
        var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
        objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2");
        objObjectIds.Add(objObjectId);
        objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
        objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);

        string templateName = "MHM Template";
        CX509ExtensionTemplateName template = new CX509ExtensionTemplateName();
        template.InitializeEncode(templateName);
        objPkcs10.X509Extensions.Add((CX509Extension)template);

        var objDN = new CX500DistinguishedName();
        var subjectName = "CN = shaunxu.me, OU = ADCS, O = Blog, L = Beijng, S = Beijing, C = CN";
        objDN.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
        objPkcs10.Subject = objDN;

        CObjectId objHash = new CObjectId();
        objHash.InitializeFromAlgorithmName(
            ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, 
            ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 
            AlgorithmFlags.AlgorithmFlagsNone, 
            "SHA1");
        objPkcs10.HashAlgorithm = objHash;

        var objEnroll = new CX509Enrollment();
        objEnroll.InitializeFromRequest(objPkcs10);

        var strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
        return strRequest;
    }

I successfully create request from private key. But I need to create request from public key. Please help me. What exactly I am missing here.

Thanks in advance

anara
  • 13
  • 1
  • 3

1 Answers1

1

The purpose of a public-key certificate is to confirm the ownership of the key pair (private and public key). That means, when I present a certificate with my name on it to another party, the certification authority confirms (by signing the certificate) that the key pair in fact belongs to me. In particular, it means that I "own" the private key. (Ownership in this context means that I know it but nobody else does.)

In order to confirm this, the certification authority must be sure that I am in possession of the private key. So, any certification process must, at some point, involve the private key. I will not give away the private key but at least I have to use it in a challenge/response exchange. The public key is used to verify the validity of my response and thus the certification authority knows that I am having the private key.

Look at it a different way. The public key is publicly known. What use would it be to have a certificate that confirms that I am the "owner" of the public key? Everybody knows it, you cannot "own" the public key.

lzydrmr
  • 867
  • 5
  • 7
  • Thanks for the fast response. But the problem is that, I don't have the private key nor generate it. Generation of the keys happens on Smart Card by other company. And they send me via Web Service the parameters and public key in order to generate the certificate and return it to them. Our company owns the CA (PKI system). The requirements are that the certificate request generated in our side. What can I do in this situation? – anara Nov 19 '14 at 10:53
  • Maybe if the other company will sign the public key with our Enrollment Certificate (which our CA generated)? And we will give them our Enrollment Certificate pfx and verify it in our side, will it be possible? – anara Nov 19 '14 at 11:13
  • Since you get the information about the public key through a secure channel already (I suppose), there is no need for an additional process to establish the key ownership. - The PKCS#10 specification says ["Note 2 - The signature on the certification request prevents an entity from requesting a certificate with another party's public key."](http://tools.ietf.org/html/rfc2986). So maybe all you are missing is the signature of the request itself? Of course, this needs to be provided by the other company. – lzydrmr Nov 19 '14 at 11:37
  • Thanks again for the fast response. Then what exactly I need to do in order to generate the request. Because the current code throwing the above exception. Maybe you could provide a code sample or tell me exactly what I missing in order to generate the request – anara Nov 19 '14 at 11:40
  • 1
    Have a look at the documentation [here](http://msdn.microsoft.com/en-us/library/windows/desktop/aa377505(v=vs.85).aspx). It says "you can call the RawDataToBeSigned property to retrieve the unsigned CertificationRequestInfo object." You have to get this one signed by the smart card, and then provide the data to the PKCS#10 object. (The technical details for doing this elude me.) The RawData property must contain the signed request data eventually. – lzydrmr Nov 19 '14 at 11:48
  • Thanks @lzydrmr for the help. I am currently reading all the CertEnroll documentation. The problem is that the other company does not allow to use the private keys. That is why I am trying to find a way without the actual private key. In the documentation I saw this line: [A PKCS #10 request must be signed by the associated private key or null-signed if it is a cross-certification request.](http://msdn.microsoft.com/en-us/library/windows/desktop/aa377505(v=vs.85).aspx). As you can see **null signed**. So I believe it is possible. I found this link, but it is using CryptoApi and not CertEnroll – anara Nov 19 '14 at 13:49
  • I think it could be possible if the API would support it, but I don't believe this is the case. Note that cross-certification is intended for use between CAs, so it's a different use-case. Good luck! – lzydrmr Nov 19 '14 at 14:59
  • Please look at this link: [Creating a Null Signed PKCS#10 Request Using CryptoAPI](http://msdn.microsoft.com/en-us/library/ms867026.aspx#certenroll_topic13). If I understood correct, it shows how to create request from public key. It uses CryptoAPI (CAPICOM), but I'm using CertEnroll in my project. Unfortunately I am not finding all the methods that required in CertEnroll. – anara Nov 19 '14 at 19:57
  • It also says that the MS CA will reject null-signed requests (while other CAs might accept them). So it really depends on your environment. – lzydrmr Nov 20 '14 at 13:45