locked
CryptoAPI CSP private keys are not deleted on filesystem. Security risk on CryptoAPI. How can i know which one to delete? RRS feed

Answers

  • Finally, i got it. Much more simpler and easier.

    I have used the CryptFindCertificateKeyProvInfo function, which looks for the desired cert on all containers, and deleted the provider-container using CRYPT_DELETEKEYSET.

    Thanks a lot for your time and help.

    • Marked as answer by helpcrypto Wednesday, December 14, 2011 3:15 PM
    Wednesday, December 14, 2011 3:15 PM
  • CryptFindCertificateKeyProvInfo tries to acquire the handle to the private key. If the private key is on a smartcard and the smartcard with that private key is not inserted into the reader then you will get a prompt. When you try to use the private key to sign or encrypt you will get a pin prompt.

     

    Using the FIND_SILENT flag will cause the CryptFindCertificateKeyProvInfo operation to fail without popping UI.

     

    Andrew

    • Proposed as answer by Andrew Bernat Thursday, January 19, 2012 1:38 AM
    • Unproposed as answer by helpcrypto Thursday, January 19, 2012 9:44 AM
    • Marked as answer by helpcrypto Thursday, January 19, 2012 9:45 AM
    Thursday, January 19, 2012 1:38 AM

All replies

  • Get the PCCERT_CONTEXT for the cert and key that you want to delete. Get the property [CertGetCertificateContextProperty] to get the CERT_KEY_PROV_INFO_PROP_ID property on the cert. This will return a structure that contains the information you will need to call CryptAcquireContext to acquire a handle to the private key. Except, you will pass CRYPT_DELETEKEYSET to CryptAcquireContext along with the container name from the CRYPT_KEY_PROV_INFO structure. This will delete the key associated with the cert.

    If the cert/key are on a card then you will also have to authenticate to the card by entering your pin.

    Andrew

    Saturday, December 3, 2011 3:21 AM
  • In first place, thanks a lot for your help with this issue. Much appreciated.

    I though i understand you well...going to code and try, and will report here later.

    In my case, the cert has been deleted from the store using CertDeleteCertificateFromStore, so only the private key is left.

    How can i locate the key? Does the private key contains a reference (like a hash) for public key?

     

    Thx again.

     

    Edit #1:

    As i was expecting, doing:

     

    //bytes=cert bytes, without the private key
    
    certCtx = CertCreateCertificateContext(X509_ASN_ENCODING,bytes,length);
    result=CertGetCertificateContextProperty(certCtx,CERT_KEY_PROV_INFO_PROP_ID,NULL,&pcbData);

     

    results in CRYPT_E_NOT_FOUND.

    Maybe i have to open another way/context?

    Maybe i should set bytes to point my private key and "look for it" on the MY store?

    Any other ideas...?

     

    Edit #2:

    I have found an VERY interesting flag called CERT_PVK_FILE_PROP_ID, which should return the file name of the encrypted file that i'm trying to delete.

    Doing:

    while(pCertContext = CertEnumCertificatesInStore(systemStore,pCertContext))
    {
        if(CertGetNameString(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,0,NULL,pszNameString,128))
        {
            printf("Cert for %s \n",pszNameString);
        }
        pcbData=0;
        if(!CertGetCertificateContextProperty(pCertContext,CERT_PVK_FILE_PROP_ID,NULL,&pcbData))
        {
            printf("ERROR: CertGetCertificateContextProperty Memory: %lu.\n",GetLastError());
        }
    }
    shows the certs installed on "MY", but gives ERROR (CRYPT_E_NOT_FOUND), like my cert doesnt have thatt property.

    Ideas?

    • Edited by helpcrypto Monday, December 5, 2011 12:02 PM edit #2: CERT_PVK_FILE_PROP_ID
    Monday, December 5, 2011 8:36 AM
  • In your edit #1, you are creating a cert context from a cert file. This is the wrong thing to do. A cert file [.cer, .crt] will not have properties. Properties only exist on certs that live in a certificate store like the "MY" store.

    Before you delete the certificate from the store, you should get the key prov info property. If it doesn't exist then that cert does not have a private key associated with it. It means one of two things: There is no private key OR there is a private key but it just isn't "hooked" up to the cert.  You can use certutil -repairstore to repair a broken connection if it is possible to do. certutil [-user or -machine] -store My <thumbprint> will test the cert to see if the private key is usable.

    One thing you may want to consider is that if you have multiple certificates that share the same key then you may end up deleteing a cert and its private key and orphaning the other certificates. If that is a problem then you may want to check all certs in your store to see if the container name of the private key matches the one you are about to delete.

     

    Andrew

    Tuesday, December 6, 2011 3:58 AM
  • Always first, and once again: thanks a lot for your help, time and patience.

    On edit #1 i was using CertCreateCertificateContext, but i thought that wasnt the correct approach and went to edit #2 CertEnumCertificatesInStore. Anyway, both cause the same CRYPT_E_NOT_FOUND error.

    Actually i have installed 2 different certificates on MY store (with different keys), and one of them in the card. The target its to delete the public and private keys from store thats already present in the card.

    doing:

    certutil.exe -user -store MY

    returns ok (Encryption test passed) for both certificates on MY store. is that correct? or i should invoke certutil -repairstore ?

    Anyhow CertGetCertificateContextProperty(pCertContext,CERT_PVK_FILE_PROP_ID,NULL,&pcbData)) still fails with CRYPT_E_NOT_FOUND for both certificates.

    Going to work on that, ill update later.

     

    Edit #3:

    Ok. I have been working more time on this, and make some interesting progress. Heres the code more or less:

    (sorry for the un-styling, but MS today is buggy, and dont let me access html/code features)

     

    //Open the store and list certs

    systemStore = CertOpenSystemStore(provider, "MY");

    while((pCertContext = CertEnumCertificatesInStore(systemStore,pCertContext)))

    {

        if(CertGetNameString(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,0,NULL,pszNameString,128))
        {
            printf("cert name: %s \n",pszNameString);
        }

        while((dwPropId = CertEnumCertificateContextProperties(pCertContext,dwPropId)))
        {
            printf("prop: %d\n", dwPropId);
        }

    //TODO compare cert to check if its the one im looking for

        //Retrieve CERT_KEY_PROV_INFO_PROP_ID

        if(CertGetCertificateContextProperty(pCertContext,CERT_KEY_PROV_INFO_PROP_ID,pvData,&pcbData))
        {

            CRYPT_KEY_PROV_INFO ckpi = *(CRYPT_KEY_PROV_INFO*)pvData;

            char* providerName = new char [wcslen(ckpi.pwszProvName) + 1];
            wsprintfA ( providerName, "%S", ckpi.pwszProvName);

            printf("%s.\n",providerName);  //prints a correct {NUMBER-OF-CONTAINER}

            if(!CryptAcquireContext(&phProv, NULL, providerName, ckpi.dwProvType, CRYPT_DELETEKEYSET))
            {
                printf("CryptAcquireContext: %lu.\n",GetLastError());
            }

        }

    }

     

    So far, the code finally runs well but CryptAcquireContext fails with 0x80090016 = NTE_BAD_KEYSET.

    According to http://msdn.microsoft.com/en-us/library/windows/desktop/aa379886%28v=VS.85%29.aspx, and considering the key container is being returned by the cert itself, seems to be an access rights issue. Am i right?

    A few questions come to miy mind:

    • If its an access rights issue...How can the SECURITY_DESCRIPTOR part be done? (I have never done something similar, and in fact i dont understand it very well). Could you provide some code-help as before?
    • Will this, delete the public part and the private key file stored on %APPDATA%\Microsoft\Crypto...?
    • If the certificate was previously deleted using CertDeleteCertificateFromStore, the CertGetCertificateContextProperty will fail, isnt it?. Then, to solve  it, a repairstore and a retry of this steps will also delete the key file, right?

    Dont forget that what im looking its to delete the private key(file) stored on local computer, which could be a security threat.

    Thx again for your help, you are being of an incredible help.

    • Edited by helpcrypto Wednesday, December 7, 2011 11:43 AM edit #3
    Wednesday, December 7, 2011 8:13 AM
  • Some comments:

     - You need to pass the container name to the CryptAcquireContext. That is how it finds the private key.

    - Since you are using CRYPT_DELETKEYSET, you shouldn't need the first phProv param in CryptAcquireContext. Read the msdn document carefully about this flag.

    - If the cert was deleted, then the key is orphaned. You would need another method to figure out what key to delete. certutil -repairstore ONLY helps you if you have an orphaned key AND a cert that does not have a key prov info [or a correct key prov info] property on the cert context. This is not your scenario from what I understand.

    - I don't understand what you mean by SECURITY_DESCRIPTOR. For instanced, a private key that lives on a smartcard as no security descriptor associated with it.

    - Once you get this code to work, it will delete the capi container which contains the public/private key. However, the certificate will still exist and it will "point" to a non-existent key.

     

    Andrew

    Thursday, December 8, 2011 4:50 AM
  • Hello Andrew. Thanks a lot for your message, going to answer inline.
    - You need to pass the container name to the CryptAcquireContext. That is how it finds the private key.

    My mistake. Changed to containerName and tested again. Its something like "{B12620CB-B039-4666-950B-54117C5CB0EF}" a valid one? (with or without curly braces?).
    Using this container name, obtained the same way as providerName, i get a 0x80090019 = NTE_KEYSET_NOT_DEF. Have to work more on this issue, ill update later.

    - Since you are using CRYPT_DELETKEYSET, you shouldn't need the first phProv param in CryptAcquireContext. Read the msdn document carefully about this flag.

    Already done that. If i understand correctly: "When this flag is set, the value returned in phProv is undefined, and thus, the CryptReleaseContext function need not be called afterward.", doesnt mean a NULL should be used, right?

    - If the cert was deleted, then the key is orphaned. You would need another method to figure out what key to delete. certutil -repairstore ONLY helps you if you have an orphaned key AND a cert that does not have a key prov info [or a correct key prov info] property on the cert context. This is not your scenario from what I understand.

    As i have only one cert in the card (probably the missing one on store), i can add the .cer to the store and try -repairstore.
    If that doesnt work, is there something like gnupg "--try-all-secrets"?

    - I don't understand what you mean by SECURITY_DESCRIPTOR. For instanced, a private key that lives on a smartcard as no security descriptor associated with it.

    was receiving a NTE_BAD_KEYSET. As the name was (incorrectly set to provider) given by the cert context, the container should exist. Then, according to CryptAcquiereContext, this could be an access rights issue. Thats the reason why i started looking at this documentation also. Now im getting another error, so maybe this is not interesting anymore.

    - Once you get this code to work, it will delete the capi container which contains the public/private key. However, the certificate will still exist and it will "point" to a non-existent key.

    The certificate can be deleted using CertDeleteCertificateFromStore, isnt it? Cant i delete the cert before deleting the key container and let the system "cleaner"? (Anyway, what im interested in its to delete the private key file on %APPDATA%)

    Ill tell you later my progress (if any), Thanks a million for your help.


    Edit#4: Success!

    Ok, here we are at the moment:

    Case A: If a user has a cert (public key only) on the store & also in card, it can easily be deleted from the store with CertDeleteCertificateFromStore.

    Case B: If a user imports a pkcs#12 on a card, and on the browser, when the card is inserted it will look for the matching cert, and delete the cer+private key(+file). Thats working great with the steps described on previous posts. (Thanks, thanks, thanks Andrew!).

    Case C: If a private key is left on the system orphan, i cant delete it.
    I have tought the following steps:

    -Install cert again on store with the same provider name (can i know the container name for the orphan keys?).
    In my case "Microsoft Base Cryptographic Provider v1.0"
    -Try to repair the store to "recover" the keypair.
    -Do as in Case B

    this seem to work, but i have used certutil. Do yo know if theres another way of doing it?
    is there any cryptoAPI call to repair the cert store?
    any other options?
    can i invoke "delete all orphans"?

    Thanks a lot again, casue with your help i have been able to do most i needed.


    • Edited by helpcrypto Friday, December 9, 2011 11:31 AM certutil trick works!
    Friday, December 9, 2011 9:02 AM
  • Congrats on getting it to work.

    To answer your last question: How to delete keys that have been orphaned.

    You can do that as follows:

    - Call CryptEnumProviders to enumerate all providers on the system.

    - For each provider call CrypGetProvParam with PP_ENUMCONTAINERS to enumerate all containers for that provider.

    - For each container name, you need to call CryptAcquireContext with the container name. This will return an HCRYPTPROV.

    - Use the HCRYPTPROV from the previous step to call CryptExportKey with blob type PUBLICKEYBLOB. This will get you the public key.

    - Search through all your certificate stores for any cert with the same public key. If you find a cert that has a matching public key then it is most likely that the private key is not totally orphaned and you may not want to delete it. If the cert has a KEY_PROV_INFO property whose container name is the same as the container name of the private key then the private key is definitely not orphaned. If the cert has no property then you could probably "repair" it using certutil -repairstore. Or you could fix it programmatically. Since you know most of the info of the key prov info and could set the property on the cert context.

     of course this is only for capi keys. You can do something similar for cng keys.

    Andrew


    • Proposed as answer by Andrew Bernat Saturday, December 10, 2011 4:00 AM
    • Edited by Andrew Bernat Saturday, December 10, 2011 4:41 AM update about capi and cng
    • Unproposed as answer by helpcrypto Wednesday, December 14, 2011 3:15 PM
    Saturday, December 10, 2011 4:00 AM
  • Hi there in a new week!

    Before continuing with my tricky way of importing a fake cert + 'certutil -repairstore' + remove, i have been trying your approach.

    Actually, im have been able of doing the following:

    1. CryptEnumProviders
    2. CryptAcquireContext with CRYPT_VERIFYCONTEXT and container=NULL for each provider
    3. PP_ENUMCONTAINERS for each provider (some buggy code related to FIRST/NEXT)
    4. CryptAcquireContext again (this time with container name) for each provider-container

    But, when going to call CryptExportKey i dont have [in] HCRYPTKEY hKey, as stated in doc http://msdn.microsoft.com/en-us/library/windows/desktop/aa379931%28v=vs.85%29.aspx.

    Today i didnt have more time to code, but will continue tomorrow for sure.

    Thx again for your advices, patience and time.

    Edit #5:
    In fact, the buggy code seems to be quite logical: I need a provider handle to enum containers, which will be used to obtain a new provider. I cannot re-use provider cause FIRST/NEXT pointers are broken.

     


    • Edited by helpcrypto Monday, December 12, 2011 4:53 PM edit#5
    Monday, December 12, 2011 4:41 PM
  • Monday, December 12, 2011 5:28 PM
  • Use CryptGetUserKey to get an HKEY. http://msdn.microsoft.com/en-us/library/windows/desktop/aa380199(v=VS.85).aspx
    That returns 0x8009000D =? NTE_NO_KEY. (I havent tested yet with AT_SIGNATURE)
    Should i remember the key doesnt exists? (has been deleted, but its crypted on the filesystem)
    Monday, December 12, 2011 5:41 PM
  • CAPI containers can have a signing and encryption key or just one signing key or one encryption key. I don't think you can have neither. So you should try both AT_SIGNATURE and AT_EXCHANGE with CryptGetUserKey api.

    I dont understand what you mean by: the key doesn't exist.

    didn't you just enumerate it?

    Andrew

    Tuesday, December 13, 2011 3:45 AM
  • Finally, i got it. Much more simpler and easier.

    I have used the CryptFindCertificateKeyProvInfo function, which looks for the desired cert on all containers, and deleted the provider-container using CRYPT_DELETEKEYSET.

    Thanks a lot for your time and help.

    • Marked as answer by helpcrypto Wednesday, December 14, 2011 3:15 PM
    Wednesday, December 14, 2011 3:15 PM
  • Hot news!

    Testing these features on clients, we found an unexpected behaviour.

    When invoking CryptFindCertificateKeyProvInfo, a Windows message is displayed (sometimes...have to guess why not always) asking to insert the smartcard.

    On details button it says something like:

    "A smartcard has been detected but it isnt the correct one for the current operation. Its possible that the smartcard you're using dosnt have the needed driver or certificate..." (Sorry if incorrect, translated)

    I have been able to ignore the issue, using CRYPT_FIND_SILENT_KEYSET_FLAG, but should i do another thing?

    • Edited by helpcrypto Wednesday, January 18, 2012 3:49 PM added flag
    Wednesday, January 18, 2012 3:40 PM
  • CryptFindCertificateKeyProvInfo tries to acquire the handle to the private key. If the private key is on a smartcard and the smartcard with that private key is not inserted into the reader then you will get a prompt. When you try to use the private key to sign or encrypt you will get a pin prompt.

     

    Using the FIND_SILENT flag will cause the CryptFindCertificateKeyProvInfo operation to fail without popping UI.

     

    Andrew

    • Proposed as answer by Andrew Bernat Thursday, January 19, 2012 1:38 AM
    • Unproposed as answer by helpcrypto Thursday, January 19, 2012 9:44 AM
    • Marked as answer by helpcrypto Thursday, January 19, 2012 9:45 AM
    Thursday, January 19, 2012 1:38 AM