aethercert
Documentation

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.

FieldValue
ArticleAEK443001
Applies toCitrix NetScaler ADC deploy target (NITRO over the NSIP)
Reproduced onNetScaler 14.1 build 73.41
CauseNo ECC curves on the nshttps-127.0.0.1-443 service, so no ECDHE cipher can be negotiated
ResolutionBind 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 failure when connecting to the NSIP.
  • Issuing the certificate succeeds. Only the deployment fails.
  • The NetScaler GUI in a browser and NITRO calls from curl.exe or 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.

  1. HTTPS access to the NSIP runs through the internal services nshttps-127.0.0.1-443 and nshttps-::1l-443.
  2. The DEFAULT cipher group is bound there, but no ECC curve. Without a curve the NetScaler cannot negotiate an ECDHE cipher.
  3. That leaves only the ciphers in DEFAULT that use plain RSA key exchange, such as AES128-SHA or AES256-GCM-SHA384.
  4. The aethercert agent does not offer ciphers with plain RSA key exchange. They provide no forward secrecy and are considered outdated.
  5. 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 parameter shows under Default profile whether 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
exit

The first call ends with a handshake failure. The second one connects.

Take a backup or snapshot first

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-SHA256

2. 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 ENABLED

The 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 DEFAULT

Resolution 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-SHA256

2. 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_384

3. Bind the cipher group:

bind ssl service nshttps-127.0.0.1-443 -cipherName CG_AETHERCERT
bind ssl service nshttps-::1l-443 -cipherName CG_AETHERCERT

4. 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 DEFAULT

Verify and retry the deploy

After the change the ECDHE test succeeds, and the deploy job goes through with no other adjustment.

  1. Repeat the OpenSSL cross-check from Diagnosis. The ECDHE test now shows Cipher is ECDHE-RSA-AES128-GCM-SHA256. If DEFAULT was unbound, the AES128-GCM-SHA256 test now fails on purpose, because CG_AETHERCERT contains no RSA key exchange.

  2. Save the configuration:

    save ns config
  3. 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

  • DEFAULT still contains SHA1-CBC ciphers and RSA key exchange ciphers. CG_AETHERCERT can therefore become the house standard for SSL virtual servers too. With resolution A, that goes through the ns_default_ssl_profile_frontend profile.
  • With the default profile enabled, the NetScaler binds ns_default_ssl_profile_internal_frontend_service to 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).

Sources

On this page