AEK443001: NetScaler TLS handshake failure caused by missing ECC curves
Deploying to a Citrix NetScaler fails with remote error: tls: handshake failure because no ECC curves are bound to the management service. Cause, diagnosis and two ways to fix it.
The aethercert agent cannot deploy a certificate to a Citrix NetScaler when no ECC curves are bound to the NetScaler's management service. The fix is made on the NetScaler. Nothing changes on the agent or the deploy target.
| Field | Value |
|---|---|
| Article | AEK443001 |
| Applies to | Citrix NetScaler ADC deploy target (NITRO over the NSIP) |
| Reproduced on | NetScaler 14.1 build 73.41 |
| Cause | No ECC curves on the nshttps-127.0.0.1-443 service, so no ECDHE cipher can be negotiated |
| Resolution | Bind ECC curves and an ECDHE cipher group |
Symptom
The deploy step to the NetScaler target fails with a TLS error before any NITRO call is made.
- The agent log shows
remote error: tls: handshake failurewhen connecting to the NSIP. - Issuing the certificate succeeds. Only the deployment fails.
- The NetScaler GUI in a browser and NITRO calls from
curl.exeor PowerShell keep working, which is why the problem goes unnoticed day to day.
Cause
The NetScaler and the agent share no cipher, because no ECC curves are bound to the management service.
- HTTPS access to the NSIP runs through the internal services
nshttps-127.0.0.1-443andnshttps-::1l-443. - The
DEFAULTcipher group is bound there, but no ECC curve. Without a curve the NetScaler cannot negotiate an ECDHE cipher. - That leaves only the ciphers in
DEFAULTthat use plain RSA key exchange, such asAES128-SHAorAES256-GCM-SHA384. - The aethercert agent does not offer ciphers with plain RSA key exchange. They provide no forward secrecy and are considered outdated.
- With no cipher in common, the NetScaler ends the handshake with
handshake_failure.
Browsers and Windows tools (SChannel) still offer RSA key exchange, so they get through.
Diagnosis
The cause is confirmed when no ECC curves appear on the service and the ECDHE test fails while the RSA test succeeds. Every command here is read-only.
1. Check the service and the SSL parameters (NetScaler CLI):
show ssl service nshttps-127.0.0.1-443
show ssl parameter- The service output has no lines like
ECC Curve Name: P_256. show ssl parametershows underDefault profilewhether Enhanced SSL Profiles are enabled. That decides between resolution A and resolution B.- If profiles are enabled, also check
show ssl profile ns_default_ssl_profile_internal_frontend_service.
2. Cross-check with OpenSSL (on the NetScaler shell, replace <NSIP>):
shell
openssl s_client -connect <NSIP>:443 -tls1_2 -cipher ECDHE-RSA-AES128-GCM-SHA256 </dev/null
openssl s_client -connect <NSIP>:443 -tls1_2 -cipher AES128-GCM-SHA256 </dev/null
exitThe first call ends with a handshake failure. The second one connects.
Take a backup or snapshot first
Before running the commands below, back up the NetScaler configuration or take a snapshot of the VM.
Resolution A: with Enhanced SSL Profiles
This resolution applies when show ssl parameter shows ENABLED under Default profile. The
curves and the cipher group are then bound once, on the management service's profile. The cipher
group can then serve as the house standard for other profiles.
1. Create the cipher group (TLS 1.3 and ECDHE-GCM for RSA and ECDSA certificates):
add ssl cipher CG_AETHERCERT
bind ssl cipher CG_AETHERCERT -cipherName TLS1.3-AES256-GCM-SHA384
bind ssl cipher CG_AETHERCERT -cipherName TLS1.3-CHACHA20-POLY1305-SHA256
bind ssl cipher CG_AETHERCERT -cipherName TLS1.3-AES128-GCM-SHA256
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-ECDSA-AES256-GCM-SHA384
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-ECDSA-AES128-GCM-SHA256
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-RSA-AES256-GCM-SHA384
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-RSA-AES128-GCM-SHA2562. Update the management service's profile:
bind ssl profile ns_default_ssl_profile_internal_frontend_service -eccCurveName X_25519
bind ssl profile ns_default_ssl_profile_internal_frontend_service -eccCurveName P_256
bind ssl profile ns_default_ssl_profile_internal_frontend_service -eccCurveName P_384
bind ssl profile ns_default_ssl_profile_internal_frontend_service -cipherName CG_AETHERCERT -cipherPriority 1
set ssl profile ns_default_ssl_profile_internal_frontend_service -tls13 ENABLEDThe curves are bound one by one rather than with ALL, which keeps the weak P_224 curve out.
3. Optional: unbind DEFAULT. The deploy does not need it: with the curves bound, the
ECDHE ciphers in DEFAULT can be negotiated too. Unbinding it hardens the service further,
because only the ciphers in CG_AETHERCERT are offered afterwards:
unbind ssl profile ns_default_ssl_profile_internal_frontend_service -cipherName DEFAULTResolution B: without Enhanced SSL Profiles
If profiles are not enabled, bind the curves and the cipher group directly to both management services. This changes only those two services, and existing sessions stay up.
1. Create the cipher group (TLS 1.3 and ECDHE-GCM for RSA and ECDSA certificates). If
CG_AETHERCERT already exists, skip this step:
add ssl cipher CG_AETHERCERT
bind ssl cipher CG_AETHERCERT -cipherName TLS1.3-AES256-GCM-SHA384
bind ssl cipher CG_AETHERCERT -cipherName TLS1.3-CHACHA20-POLY1305-SHA256
bind ssl cipher CG_AETHERCERT -cipherName TLS1.3-AES128-GCM-SHA256
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-ECDSA-AES256-GCM-SHA384
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-ECDSA-AES128-GCM-SHA256
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-RSA-AES256-GCM-SHA384
bind ssl cipher CG_AETHERCERT -cipherName TLS1.2-ECDHE-RSA-AES128-GCM-SHA2562. Bind the curves:
bind ssl service nshttps-127.0.0.1-443 -eccCurveName X_25519
bind ssl service nshttps-127.0.0.1-443 -eccCurveName P_256
bind ssl service nshttps-127.0.0.1-443 -eccCurveName P_384
bind ssl service nshttps-::1l-443 -eccCurveName X_25519
bind ssl service nshttps-::1l-443 -eccCurveName P_256
bind ssl service nshttps-::1l-443 -eccCurveName P_3843. Bind the cipher group:
bind ssl service nshttps-127.0.0.1-443 -cipherName CG_AETHERCERT
bind ssl service nshttps-::1l-443 -cipherName CG_AETHERCERT4. Optional: unbind DEFAULT. The deploy does not need it; unbinding it hardens the
services further. Only after step 3, so the services are never left without a cipher:
unbind ssl service nshttps-127.0.0.1-443 -cipherName DEFAULT
unbind ssl service nshttps-::1l-443 -cipherName DEFAULTVerify and retry the deploy
After the change the ECDHE test succeeds, and the deploy job goes through with no other adjustment.
-
Repeat the OpenSSL cross-check from Diagnosis. The ECDHE test now shows
Cipher is ECDHE-RSA-AES128-GCM-SHA256. IfDEFAULTwas unbound, theAES128-GCM-SHA256test now fails on purpose, becauseCG_AETHERCERTcontains no RSA key exchange. -
Save the configuration:
save ns config -
In the aethercert dashboard, open Manage > Jobs and press Retry deploy on the failed job. The agent already has the certificate, so only the deploy step runs. See Certificate jobs.
Notes
DEFAULTstill contains SHA1-CBC ciphers and RSA key exchange ciphers.CG_AETHERCERTcan therefore become the house standard for SSL virtual servers too. With resolution A, that goes through thens_default_ssl_profile_frontendprofile.- With the default profile enabled, the NetScaler binds
ns_default_ssl_profile_internal_frontend_serviceto the internal services and disables SSLv3, TLS 1.0 and TLS 1.1 there. - Replacing a certificate on the NetScaler by hand: Citrix NetScaler (ADC).