EC P-256 + TLS 1.2 Client Authentication Fails with CKR_GENERAL_ERROR #123
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Environment
When using an EC P-256 (prime256v1) key stored on PicoHSM for TLS 1.2 client certificate authentication, the PKCS#11 library returns CKR_GENERAL_ERROR during the TLS handshake signing operation.
The same key works correctly with TLS 1.3.
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
--login --pin
--keypairgen --key-type EC:prime256v1
--label "client-ec256-a" --id 05
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
--login --pin
--write-object cert.der --type cert --id 05 --label "client-ec256-a"
Expected Behavior
TLS handshake succeeds and client is authenticated.
Actual Behavior
sun.security.pkcs11.wrapper.PKCS11Exception: CKR_GENERAL_ERROR
The error occurs during the signing operation in the TLS handshake.
Working Configuration
Additional Notes
The same test with YubiHSM2 (using yubihsm_pkcs11.so) works correctly with EC P-256 + TLS 1.2, suggesting this is specific to the PicoHSM/OpenSC implementation.
The difference between TLS 1.2 and TLS 1.3 ECDSA signing:
Can you provide the steps with Java provider?
Is the problem only with P256+TLS1.2? specifically?
Did you test with another P256 key? Happens with always?
Hi Pol, thank you for looking into this.
1. Java Provider Configuration
I'm using the SunPKCS11 provider with this configuration:
name = PicoHSM
library = /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
slot = 0
The TLS connection is established via standard Java SSLContext with the PKCS#11 KeyStore. I can provide a minimal reproducer if helpful.
2. Confirmation: Yes, specifically P256 + TLS 1.2
CKR_GENERAL_ERROR3. Consistency: Yes, reproducible with multiple keys
I tested with freshly generated P-256 keys - same behavior every time.
Additional diagnostic information that may help:
The key difference between TLS 1.2 and 1.3 for ECDSA signing:
signature_algorithms(e.g.,ecdsa_secp256r1_sha256) - the mechanism is clearQuestion: Could it be that TLS 1.2 is requesting a raw ECDSA signature (
CKM_ECDSA) with pre-hashed data, while TLS 1.3 usesCKM_ECDSA_SHA256? If PicoHSM expects a specific mechanism or has constraints on input data length forCKM_ECDSA, that might explain why one works and the other doesn't.I can enable PKCS#11 debug logging (
OPENSC_DEBUG=9) to capture the exactC_Signcall parameters if that would help identify what mechanism/data is being passed.It could be. Perhaps also happens with P384 or P521. If you enable OPENSC_DEBUG=9 please provide the log around where it fails with APDU messages.
OPENSC_DEBUG=9 Log for EC P-256 TLS 1.2 Failure
Test scenario: EC P-256 key with TLS 1.2 (CKM_ECDSA_SHA256)
Summary
Comparison with working EC P-384 TLS 1.2
The difference is that P-384 sends pre-hashed data (48 bytes), while P-256 sends the full data (6039 bytes) for internal hashing.
Detailed APDU trace around failure
06:10:41.801 mechanism.c:383:sc_pkcs11_sign_init: mechanism 0x1044, key-type 0x3
06:10:41.801 pkcs11-object.c:697:C_SignInit: C_SignInit() = CKR_OK
06:10:41.801 mechanism.c:570:sc_pkcs11_signature_update: data part length 6039
06:10:41.801 framework-pkcs15.c:4270:pkcs15_prkey_sign: Initiating signing operation, mechanism 0x1044.
06:10:41.802 apdu.c:367:sc_single_transmit: CLA:80, INS:68, P1:5, P2:73, data(6039)
06:10:41.802 reader-pcsc.c:325:pcsc_transmit: Outgoing APDU (6048 bytes)
06:10:41.879 reader-pcsc.c:272:pcsc_internal_transmit: SCardTransmit/Control failed: 0x80100016
06:10:41.888 apdu.c:380:sc_single_transmit: unable to transmit APDU: -1107 (Transmit failed)
06:10:41.888 card-sc-hsm.c:1128:sc_hsm_compute_signature: APDU transmit failed
06:10:41.970 pkcs11-object.c:809:C_SignFinal: C_SignFinal() = CKR_GENERAL_ERROR
Environment
Notes
The issue appears to be specific to sending large payloads (6KB) to the HSM for internal hashing with EC P-256.
Maximum payload is 2KB. 6KB seems a lot to be signed in one transaction. Not sure why tries to do a raw sign with P256 but not with P384.
The tests work fine with SoftCertificate and YubiHSM 2. Can we increase the maximum payload size?
What does
pkcs11-tool -Mreturns with YubiHSM2?After extensive debugging, the root cause is now clearly identified and confirmed.
When using OpenSC’s
opensc-pkcs11.sowith thesc-hsmdriver, the module advertises combined digest+sign mechanisms such asCKM_ECDSA_SHA256. In TLS 1.2, OpenSSL is allowed to pass the full CertificateVerify input to PKCS#11 backends implementing such mechanisms. This input is not a hash, but the raw preimage, whose size depends on the handshake transcript and is effectively unbounded (in practice several kilobytes, but not guaranteed to stay small).OpenSC forwards this buffer verbatim to the card using a single APDU. Even if the firmware supports APDU chaining, OpenSC does not fragment or stream this operation in this code path. As a result, small embedded devices must either allocate arbitrarily large buffers (unsafe and non-deterministic) or fail.
Increasing internal USB/APDU buffers makes the operation succeed temporarily, but this is not a robust solution: the input size is protocol- and implementation-dependent and may grow in future OpenSSL or TLS changes.
Disabling
CKM_ECDSA_SHA*in OpenSC fixes the issue by forcing OpenSSL to hash on the host and callCKM_ECDSAwith a fixed-size digest (32/48 bytes). However, requiring users to rebuild OpenSC is not acceptable.Conclusion: this is not a firmware bug, nor a TLS bug, but a mismatch between OpenSC’s
sc-hsmPKCS#11 model (designed for full SmartCard-HSM devices) and constrained embedded tokens.The correct solution in this case is to use the embedded variant of the SmartCard-HSM stack, which exposes a PKCS#11 module tailored for embedded devices and does not advertise unsafe digest+sign mechanisms.
Please load and use:
(from https://github.com/CardContact/sc-hsm-embedded)
instead of
opensc-pkcs11.so.