Opensc: SC-HSM: Creating private key for ADCS and other certificates fails w/ SCARD_E_READ_ONLY_CARD when using OpenSC CSP or MS Smart Card KSP

Created on 6 Aug 2019  ·  29Comments  ·  Source: OpenSC/OpenSC

Problem Description

I was trying to set up ADCS using a Nitrokey HSM 2. When trying to use it to create a certificate for Active Directory Certificate Services using Windows Server 2019, the process fails with error SCARD_E_READ_ONLY_CARD. This error occurs choosing OpenSC CSP/sha1/2048 (#1749 related) as well as RSA#Microsoft Smart Card Key Storage Provider/sha256/2048.

For testing, I use the 0.19-derived OpenSC build mentioned in #1688 because otherwise I'd have the same issues mentioned in this issue (read only smart card which is not selectable).

Proposed Resolution

I see that it is very much possible ADCS will never work using SC-HSM or with OpenSC in general because of reasons only Microsoft can tell us. I've seen that ADCS support is explicitly not advertised for the NK HSM2. Anyway, it would be nice if the issues could be worked around/needed features implemented or I could just get some background information why ADCS cannot work with smart cards, as I'm interested 😉. I also tried to test this with a YubiKey 5 and Yubico's Minidriver which will also not work (although key generation and the setup process run fine or at least complete).

Steps to reproduce

Initialize token (this one was actually initialized using dkek shares). Install Windows Server, Active Directory Certificate Services and the OpenSC build from #1688 .
Configure OpenSC like shown below and change the ATR for SC-HSM inside the registry.
Try to go through the setup assistant, choosing to generate a new CA with a new private key.
Choose OpenSC CSP or MS SC KSP. Allow Administrator Interaction.
Try to complete the assistant by choosing the NitroKey HSM smart card twice (note that there's no PIN request at all)

edit: This error is not specific to ADCS and can be easily reproduced on every recent Windows machine by running the New-SelfSignedCertificate PowerShell cmdlet (you may need to have administrative privileges):

New-SelfSignedCertificate -DnsName "www.example.com" -CertStoreLocation cert:\LocalMachine\My" -Provider "OpenSC CSP"

New-SelfSignedCertificate -DnsName "www.example.com" -CertStoreLocation cert:\LocalMachine\My" -Provider "Microsoft Smart Card Key Storage Provider"

Logs

Configuration (ATR was updated in registry as well):

app default {
    debug = 3;
    debug_file = c:/tmp/opensc-debug.txt;
    framework pkcs15 {
        # use_file_caching = true;
    }
    # SmartCard-HSM 4k with contact-based interface or USB-Stick
    card_atr 3b:de:18:ff:81:91:fe:1f:c3:80:31:81:54:48:53:4d:31:73:80:21:40:81:07:1c {
        driver = "sc-hsm";
        md_read_only = false;
        md_supports_X509_enrollment = true;
        md_supports_container_key_gen = true;
        md_guid_as_label = true;
    }
}

MS SC KSP log: https://gist.github.com/marcoh00/567331f26e0bbda9991031427367e469
OpenSC CSP log: https://gist.github.com/marcoh00/a4b90ebdf55f64331da54e5523e0887e

Input needed

All 29 comments

Besides, I would love to test how AD CS behaves when key generation is completed. For this, I do have a software generated test certificate. Is it still possible to import private keys using the SDK? If yes, it would be awesome if I could obtain this somehow to make some more tests.

Years ago before retiring, I would create test PIV cards with certificates signed by our ADCS. This was done in a multi step process. Most of the steps were done on linux, with exception of using IE on windows to send request to ADCS and retrieve certificate from ADCS.

You might be able to do something similar with the Nitro using OpenSC/HSM utilities.

The PIV is in effect a SCARD_E_READ_ONLY_CARD as it needs a management 3DES or AES key to generate a key pair and to write certificates and other objects to the card.

The general idea for a key and certificate was:

  • Generate key pair on card piv-tool -A M:9B:03 -G $KEYID:$ALGID -o cards/$1.$KEYID

  • Use card to sign a Certificate request openssl req $SSLEAY_CONFIG -engine pkcs11 -keyform engine -sha256 -new -key slot_1-id_$ID -out cards/$1.myreq.$KEYID.pem -text

  • Send request to ADCS via web page "cut-and-paste" cards/$1.myreq.$KEYID.pem (I had rights to generate certificates in the ADCS for any user.)

  • Retrieve certificate from ADSC via web page "cut-and-paste" to cards/$1.cert.$KEYID.pem

  • Load certificate onto card piv-tool -A M:9B:03 -C $KEYID -i cards/$1.cert.$KEYID.pem

We had no intention of letting users generate their own cards. Cards would either be government issued PIV badge and registered in AD or in case a user's badge was: left at home, lost, stolen or broken we could issue a temporary PIV card good to access at least AD.

Hi @dengert and thanks a lot for your answer :) ! I'm actually pretty sure that these steps would work in case someone wants to provision a device w/ a certificate obtained via the ADCS webinterface so it's definitely nice to have here in case someone stumbles over this issue using Google.

Anyway, unfortunately this does not solve the problem, as this issue was (originally) about using a NK-HSM together with the Certificate Services (Server) itself for issuing new certificates/signing requests.

As I see it, now it is about generating new key pairs and using them with the CSP in general, as the New-SelfSignedCertificate cmdlet and Internet Explorer's built-in methods for generating keys ( #1688 ) do not seem to work, either.

Thanks. You may want to try the OpenSC 0.20.0-rc1 with whatever cards you have.

https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps looks interesting. Looks like it if you set -Provider" OpenSC CSP" it might work. At least it would give a simpler test to debug the minidriver.

https://docs.microsoft.com/en-us/powershell/module/pkiclient/?view=win10-ps looks very interesting. What other commands have the -provider parameter?

Hi! The last time I tried, neither OpenSC CSP nor Microsoft Smart Card KSP would work with the PowerShell cmdlet (SCARD_E_READ_ONLY_CARD). The logs looked pretty similar to the ADCS ones linked above. I will try again using the rc1 when I'm going back to work next week and collect another pair of logs while I'm at it.

Edit regarding the -Provider parameter: As far as I know, most of Microsoft's PKI cmdlets (server as well as client) which involve handling key material have it, sometimes the naming is a little bit different (e.g. Install-AdcsCertificationAuthority has -CryptoProviderName). Most of them are designed to communicate with an ADCS instance however and ADCS' "Certificate Templates" tell the client which CSP it should use. Good ol' certutil allows for specifying a CSP/KSP as well for most operations.

Hi! I was able to try again using OpenSC 0.20rc1. To my surprise, generating a certificate using Microsoft Smart Card Key Storage Provider did work this time 👍 !

What did not work is generating a self signed certificate using the OpenSC Provider. It asks for my PIN and a few moments later I'm supposed to select a smart card. When choosing "SmartCard-HSM 4K" the PIN entry dialog tells me that the requested operation could not be performed using the chosen smart card. Log: https://gist.github.com/marcoh00/dd3c58526be1abaee3601a6f475be7ea

What did not work either is to setup ADCS using Microsoft Smart Card Key Storage provider. The error is the same as with New-SelfSignedCertificate and OpenSC CSP: The requested operation could not be performed. Log: https://gist.github.com/marcoh00/e640d8f57e887c78d0310723f1093a39

I see in the first log: " HANDLES CHANGED from" and "trying to reinit card" twice. As I said in some other comment (I do not recall where) The tests in minidriver.c where this is generated maybe too aggressive and needs to be changed.

if (pCardData->hSCardCtx != vs->hSCardCtx || if (pCardData->hSCardCtx != vs->hSCard) {
needs to be changed. If only the pCardData->hScard != vs->hScard the the card does not need to reset, but the vs->hScard needs to be changed. I can't prove this is the case. It is not clear why a different handle is being provided, other then this is a different thread and vs->hScard is used for locking and the process is sharing a common pCardData->hSCardCtx.
`
I think setting the debug level to 7 would show if the one or both of the above are being changed.

Hi,

here's a log of New-SelfSignedCertificate using debug = 7: https://gist.github.com/marcoh00/412a0ce89f5ace07e8265e802398fb83

@marcoh00 have a look at #1816. It is untested. The msi files can be found under the "continuous-integration/appveyor/pr — AppVeyor build succeeded" Artifacts.

The changed will not call reinit_card_for if only the hScard handle changes.

Where exactly do I find this fixed build?

For any Pull Request (#1816 in this case), you can find the Artifacts by doing the following:

https://github.com/OpenSC/OpenSC/pull/1816

Look for where the tests were run and for Windows "continuous-integration/appveyor/pr"
Click on "Details" in this case: https://ci.appveyor.com/project/LudovicRousseau/opensc/builds/27971350

Under "Jobs" look for the versions you would like:
Look for the "Environment; VSVER=14 Configuration: Release; Platform x86"
Click on Artifacts and you will find " OpenSC-0.20.0-rc2_win64.msi"
https://ci.appveyor.com/api/buildjobs/m6u0betugpocy4qw/artifacts/OpenSC-0.20.0-rc2_win64.msi

and also look for "Environment; VSVER=14 Configuration: Release; Platform x64"
Click on "Artifacts and you will find "OpenSC-0.20.0-rc2_win64.msi"
https://ci.appveyor.com/api/buildjobs/m6u0betugpocy4qw/artifacts/OpenSC-0.20.0-rc2_win64.msi

Note retention policy: "Artifacts older than 6 months are permanently removed from AppVeyor artifact storage."

1816 was a first attempt. Looking closer, the issue may also include that if Windows passes a zero for either handle, this is considered a null handle for the CTX, a check_reader should not be called as there is no reader or card provided. But the data already read is still valid. Some extra debugging needs to be done to see it this is the case. The current code basicly drops the reader and card and starts over. This may lose the fact that a key was generated, but can then not be found by later calls to the minidriver.

Correctio to the above:
Under "Jobs" look for the versions you would like:
Look for the "Environment; VSVER=14 Configuration: Release; Platform x86"
Click on Artifacts and you will find " OpenSC-0.20.0-rc2_win64.msi"
https://ci.appveyor.com/api/buildjobs/m6u0betugpocy4qw/artifacts/OpenSC-0.20.0-rc2_win64.msi

Should read: "OpenSC-0.20.0-rc2_win32.msi"
https://ci.appveyor.com/api/buildjobs/5mduuud4ob2yda7o/artifacts/OpenSC-0.20.0-rc2_win32.msi

Thank you dengert, unfortunately the patch didn't really help.

I attached a Nitrokey HSM with the following opensc.conf:

app default {
    debug = 3;
    debug_file = c:/tmp/opensc-debug.txt;
    framework pkcs15 {
        # use_file_caching = true;
    }
    # SmartCard-HSM 4k with contact-based interface or USB-Stick
    card_atr 3b:de:18:ff:81:91:fe:1f:c3:80:31:81:54:48:53:4d:31:73:80:21:40:81:07:1c {
        driver = "sc-hsm";
        read_only = false;
        md_read_only = false;
        md_supports_X509_enrollment = true;
        md_supports_container_key_gen = true;
        md_guid_as_label = true;
    }
}

Manually adding the registry key _HKLM\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SmartCard-HSM 4K_ was no longer necessary with version 0.20rc2.

When I ran this command with OpenSC 0.19:
_New-SelfSignedCertificate -DnsName "www.example.com" -CertStoreLocation "Cert:\LocalMachine\My" -Provider "Microsoft Smart Card Key Storage Provider"_
I received the following error message:
_New-SelfSignedCertificate : CertEnroll::CX509Enrollment::_CreateRequest: An internal consistency check failed. 0x80100001 (-2146435071 SCARD_F_INTERNAL_ERROR)_
OpenSC debug log is here: opensc-debug 0.19 SCARD_F_INTERNAL_ERROR.txt

After uninstalling 0.19 and installing 0.20rc2 with your patch, the error changes to:
_New-SelfSignedCertificate : CertEnroll::CX509Enrollment::_CreateRequest: An attempt was made to end a non-existent transaction. 0x80100016 (-2146435050 SCARD_E_NOT_TRANSACTED)_
OpenSC debug log is here: opensc-debug 0.20rc2 SCARD_E_NOT_TRANSACTED.txt

For the record, when I try the same command with the providers "OpenSC CSP" or "Microsoft Base Smart Card Crypto Provider", Powershell simply crashes.

Thanks for trying the patch. There is something else going on. I will try and looks at it later this month.

Can you get the minidriver's MD.log of a failed attempt?
See registry HKLM\SOFTWARE\OpenSC Project\OpenSC\MiniDriverDebug = 1
and [HKLM\SOFTWARE\WOW6432Node\OpenSC Project\OpenSC\MiniDriverDebug = 1
The log is written to C:\tmp\MD.log

Among other things, it would show if CARD_SECURE_KEY_INJECTION_NO_CARD_MODE is set in dwFlags when CardAcquireContext is called. It appears that it may be set when "Secure Key Injection" is required. This may the case with ADCS.

minidriver.c has

    /* secure key injection not supported */
           return SCARD_E_UNSUPPORTED_FEATURE;

See minidriver_specs_v7.07.docx

Hi,

sorry for the late reply! I can reproduce gnugnug's results. I uploaded both opensc-debug files with debug=7 as well as md.log files using both relevant CSPs/KSPs:

  • OpenSC CSP [opensccsp]
  • Microsoft Smart Card Key Storage Provider [msscksp]

opensc-debug_opensccsp.txt
opensc-debug_msscksp.txt
md_opensccsp.log
md_msscksp.log

Thanks a lot for you help!

@marcoh00 can you try this version? https://github.com/OpenSC/OpenSC/pull/1848 It solves the problems with using the handles correctly and does not reinit the card every time they change, losing information about a generated key.

The VER=14 compiles but VER=12 does not support __func__ If this solves your problem, I can look at cleaning it up and do something about using __func__

Hi! Thanks a lot, this seems to work a lot better 👍

I was able to successfully generate a self signed certificate using the PowerShell cmdlet and using Microsoft Smart Card Key Storage Provider.

Using OpenSC CSP (command as above in the issue description) I got:

New-SelfSignedCertificate : CertEnroll::CX509Enrollment::_CreateRequest: Ungültige Optionen angegeben 0x80090009
(-2146893815 NTE_BAD_FLAGS)

A very wild guess would be that this could have something to do with #1749 and the cmdlet recognizing that no SHA-2 algorithms are offered initially (it seems to use sha256 if nothing else is specified). It works when using the command from your pull request (albeit it uses sha1 then, which isn't something anyone wants to do I guess). Anyway, this seems to be a different issue.

I will try again using ADCS next week and report back. But it looks good so far 😃

Sounds like NTE_BAD_FLAGS has to do with the key needs to be archived, but it is marked as not exportable (Google for NTE_BAD_FLAGS) shows these that may be related:

https://windows-hexerror.linestarve.com/q/so41916168-PowerShell-New-SelfSignedCertificate-CertEnrollCX509Enrollment_CreateRequest-Invalid-flags-specified

https://social.technet.microsoft.com/Forums/ie/en-US/944d42aa-58a8-4d7d-8ac3-cfb1a78c4a69/microsoft-ad-cs-and-safenet-hsm-integration-ntebadflags?forum=winserver8setup

Sounds like for what every political/organizational/legal policy, if this key can be used to encrypt, ADCS may want to escrow the key. So either a software key is generated and loaded onto the card, or the card generated key must be exportable.

Hi,
indeed, specifying -KeyExportPolicy NonExportable made it possible to generate a certificate using OpenSC CSP. Unfortunately I wasn't able to make it work w/ non-sha1 hash algorithms (but this works using MSSCKSP).

As I said, I will try to do some additional tests using ADCS (all of these tests were done using the PowerShell cmdlet until now) next week and open other issue(s) if needed. It seems like your PR fixed this concrete issue now. Thanks again!

Sounds like the SHA1 issue has to do with the caller.

The md.log shows the caller is passing in sha1:

CardSignData dwVersion=1, bContainerIndex=2, dwKeySpec=2, dwSigningFlags=0x00000000, aiHashAlg=0x00008004

CALG_SHA1 is 0x00008004

This g=has the same problem:
SHA-256-Self-Signed-Certificate-for-Windows-Server-2012-R2

Self-signed-certificate-5920a7c6/view/Discussions

leasd to:
New-SelfSignedCertificateEx.zip

Did not get very far with this, it still uses SHA1.

Just for the record: I was able to test the latest OpenSC release and the CI build of #1848 together with ADCS. Unfortunately it didn't work. I will be able to provide log files by the end of next week.

Shall I open a seperate issue for that? Now that I am not able to reproduce these issues with vanilla windows components anymore (like the New-SelfSignedCertificate), does anyone even want to look into this issue even further? I could offer to provide you a Windows Server 2019 Evaluation VM with everything set up to reproduce and debug the issue if you want. You would only need to tell me your preferred virtualization environment in case you're interested.

Personally I have no interest using the ADCS. I am retired and even then any smart card used with AD should be issued in some other way. The problem I see with New-SelfSignedCertificate and friends is that the can not support SHA256 because thay still use CAPI and not CAPI2. No one should be using SHA1. So unless Microsoft fixes New-SelfSignedCertificate or there is another way to use some other tool with CAPI2, OpenSC can not debug the problem.

I'm not sure I understand this: When choosing "Microsoft Smart Card Key Storage Provider" (either with New-SelfSignedCertificate or ADCS), the OpenSC minidriver is obviously in use somehow and SHA256 signatures are created just fine.

I thought about this again and I think there maybe has been a little misunderstanding (just to make it clear, so we are on the same page here):

I opened this issue because I observed that there was no way whatsoever to generate any kind of certificate and private key using the default APIs provided by Windows (either by ADCS or self-signed by New-SelfSignedCertificate). As I see it, this particular issue seems to be completely fixed by now (thanks a lot for that again!).

In the process of fixing this, we observed some other behaviors which may need fixing:

  1. When explicitly specifying the OpenSC CSP Provider by its name, Windows' CA components always seem to use CAPI, which makes it impossible to generate certificates using state-of-the-art cryptographic algorithms. This can be completely mitigated by specifying Microsoft Smart Card Key Storage Provider, which obviously uses the OpenSC minidriver in the background as well while offering all the nice CAPI2 parts.
    While it would probably be nice to know, why it behaves this way (and whether there are any downsides to using OpenSC via MSSCKSP), this could probably be solved by documenting this behavior somewhere users will find it.

  2. The ADCS component still isn't able to generate a certificate + private key which it can use afterwards. While this has probably something to do (again) with some weird usage of the APIs which are offered I don't think it has anything to do with the CAPI/CAPI2 distinction mentioned above. I also think this should be fixed if possible as CSPs of other vendors (like Yubico HSM's or those for the more expensive ones) do support this, after all. And maybe it's just another bug on OpenSC's side (like the last one), really.

That said, I see that this is an open source project after all and you're probably doing this in your spare time - I'm very grateful for your support so far and don't mean to demand anything here!

Good to here that 1. works with the mods when you use -provider "Microsoft Smart Card Key Storage Provider" but does not work when using -provider OpenSC CSP

See for a question on how to address this: https://github.com/OpenSC/OpenSC/pull/1848#issuecomment-577230037

Could it also fix question 2.? Is the ADCS code is looking in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SmartCard-HSM (or any other OpenSC driver) then using the "Crypto Provider" = "OpenSC CSP", then trying to use it with the KSP code and not finding "OpenSC CSP" in Aliases in
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Cryptography\Providers\Microsoft Smart Card Key Storage Provider assumes it is a CSP only?

@marcoh00 Could you try modifying the HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Cryptography\ProvidersMicrosoft Smart Card Key Storage Provider` aliases to add "OpenSC CSP" then see if ADCS works?

Hi! I'll do so by the end of next week when I'll have access to a test device again.

Anyway, if it happens to work using the new alias, maybe OpenSC should think about introducing a another (additional?) alias for its CSP/KSP. As far as I understood it, according to smart-card-minidrivers the current behavior is actually expected of a CSP, as the drivers for CNG are actually called KSPs.

Another thing to try, is to not use the "hack" in:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SmartCard-HSM Crypro Provider = OpenSC (Or whatever card you are using.)

and replace with: Crypro Provider = Microsoft Base Smart Card Crypto Provider

What could be happening, is the ADSC code is using Crypro Provider under the Calais
and looking at HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Cryptography\Providers\Microsoft Smart Card Key Storage Provider Aliases and not finding OpenSC CSP as an alias assumes it is not a KSP.

Hi!

Unfortunately I have not been successful with the changes mentioned here and inside the PR. Now that the PR is merged and self signed certificates can be obtained using New-SelfSignedCertificate/Windows CA infrastructure it is nice to see that the issue is closed by now :)

I suggest I'll open two additional issues soon, one for the CSP/KSP/SHA1 issues mentioned above (I've gotten around to do some API traces using the mentioned tool which I'll post then) and one for the problems I am facing while generating a CSR for ADCS to keep seperate issues seperate ;-)

Was this page helpful?
0 / 5 - 0 ratings