Configuring and Troubleshooting TLS/SSL with Informix
Informix® configuration and documentation still mostly say "SSL," but a
modern connection is normally TLS underneath — you'll see both terms
used interchangeably in onconfig, onstat output, and
the utilities themselves (onsocssl, SSL_KEYSTORE_LABEL,
and so on). This article covers configuring TLS on the server and native
client, PKCS#12 and legacy GSKit KDB keystores, Java/JDBC truststores, and a
layer-by-layer diagnostic sequence for when a connection that should work
doesn't.
The examples below use a consistent placeholder environment: DBSERVERNAME
mydatabase on prod-db01.example.com, plain TCP on port
9088 and TLS on 9089, with a PKCS#12 server keystore
at $INFORMIXDIR/ssl/mydatabase.p12 (stash file
mydatabase.sth). Older releases may use a GSKit CMS keystore
(mydatabase.kdb) instead — noted where it differs.
How the pieces fit together
A typical Informix TLS setup has a clear split between server identity and client trust:
Certificate Authority
|
+-------------+-------------+
| |
v v
Client trust Server identity
certificates certificate
+
private key
| |
v v
+--------------+ +--------------+
| Native | | Informix |
| client | | server |
| | | |
| client.p12 | | mydatabase |
| client.sth | | .p12 |
| conssl.cfg | | .sth |
+------+-------+ +------+-------+
| ^
| TLS |
+----------------------------+
Java clients normally use a Java truststore rather than conssl.cfg
— covered separately below.
Server keystore and SSL_KEYSTORE_LABEL
The server keystore is tied to DBSERVERNAME: for
DBSERVERNAME mydatabase, Informix expects
$INFORMIXDIR/ssl/mydatabase.p12 (or mydatabase.kdb on
an older GSKit-only release), with its stash file mydatabase.sth.
Confirm the configured name:
onstat -g cfg DBSERVERNAME
This is a different thing from INFORMIXSERVER, which is a
sqlhosts alias — DBSERVERNAME=mydatabase and
INFORMIXSERVER=mydatabase_ssl pointing at a TLS-specific
sqlhosts entry for that same instance is normal and expected.
A PKCS#12 or KDB file can hold more than one certificate — an old
rotated-out cert alongside the current one, plus the CA chain.
SSL_KEYSTORE_LABEL tells Informix which personal certificate inside the
keystore to present as the server's identity:
onstat -g cfg SSL_KEYSTORE_LABEL
List what's actually in the keystore and check the label matches:
gsk8capicmd_64 -cert -list -db $INFORMIXDIR/ssl/mydatabase.p12 -stashed
Certificates found
- mydatabase
! Corporate Intermediate CA
! Corporate Root CA
(- marks a personal certificate with a private key; !
marks a trusted CA-only entry.) The label named by SSL_KEYSTORE_LABEL
must be one of the - entries, not a CA.
SSL_KEYSTORE_FILE is the client-side counterpart, normally set in
$INFORMIXDIR/etc/conssl.cfg rather than onconfig —
covered under native client configuration below. The server keystore normally
contains the server certificate, its private key, and the intermediate/root CA
chain; the private key must stay protected, so keep server-keystore permissions
tight:
chown informix:informix $INFORMIXDIR/ssl/mydatabase.p12 $INFORMIXDIR/ssl/mydatabase.sth
chmod 600 $INFORMIXDIR/ssl/mydatabase.p12 $INFORMIXDIR/ssl/mydatabase.sth
A file can have correct permissions while a parent directory still blocks Informix from reaching it — check the whole path, not just the file:
namei -l $INFORMIXDIR/ssl/mydatabase.p12
Creating and populating a server keystore
Create a new PKCS#12 keystore with its stash file in one step:
cd $INFORMIXDIR/ssl
gsk8capicmd_64 -keydb -create -db mydatabase.p12 -pw 'password' -stash -type p12
This produces mydatabase.p12 and mydatabase.sth.
For production, the server certificate should be signed by a corporate or
public CA: create the private key, generate a CSR, send it to the CA, then
import the signed certificate back into mydatabase.p12. The
private key should originate in, and stay inside, the keystore where possible
— import the CA chain first:
gsk8capicmd_64 -cert -add -db mydatabase.p12 -stashed -label "Corporate Root CA" -file root-ca.pem -format ascii
gsk8capicmd_64 -cert -add -db mydatabase.p12 -stashed -label "Corporate Intermediate CA" -file intermediate-ca.pem -format ascii
The certificate itself needs the DNS names clients actually connect through — modern TLS clients validate against the Subject Alternative Name, not just the Subject CN:
Subject:
CN=prod-db01.example.com
Subject Alternative Name:
DNS:prod-db01.example.com
DNS:prod-db01
DNS:mydatabase.example.com
A certificate can be correctly signed, unexpired, and fully trusted and still
fail validation purely on a hostname mismatch — a client connecting to
prod-db01.example.com against a certificate whose only SAN is
dev-db01.example.com has a valid trust chain and the wrong server
identity. This is a separate failure mode from everything else in this article
and worth checking first when TLS negotiates but the client still rejects the
connection.
List, inspect, and extract certificates from the keystore as needed:
# list (KDB: swap -db mydatabase.p12 for mydatabase.kdb)
gsk8capicmd_64 -cert -list -db mydatabase.p12 -stashed
# full details, including Not Before / Not After
gsk8capicmd_64 -cert -details -db mydatabase.p12 -stashed -label mydatabase
# extract to a plain PEM for openssl
gsk8capicmd_64 -cert -extract -db mydatabase.p12 -stashed -label mydatabase -target /tmp/mydatabase.pem -format ascii
openssl x509 -in /tmp/mydatabase.pem -noout -subject -issuer -serial -dates
openssl x509 -in /tmp/mydatabase.pem -noout -ext subjectAltName
sqlhosts and NETTYPE
A server typically has both a plain and a TLS entry for the same instance:
mydatabase onsoctcp prod-db01.example.com 9088
mydatabase_ssl onsocssl prod-db01.example.com 9089
onsoctcp is a normal SQLI connection; onsocssl is the
TLS one. The server side needs a matching TLS listener in NETTYPE:
NETTYPE socssl,3,50,NET
Don't confuse the two settings — sqlhosts uses
onsocssl, NETTYPE uses socssl (no "on"
prefix).
Native client configuration
DB-Access, ESQL/C, the Informix CSDK, ODBC, dbschema,
dbexport/dbimport/dbload — all the
native clients read the same TLS configuration file,
$INFORMIXDIR/etc/conssl.cfg:
SSL_KEYSTORE_FILE /opt/ibm/informix/etc/client.p12
SSL_KEYSTORE_STH /opt/ibm/informix/etc/client.sth
A client keystore only needs the trusted root and intermediate CA certificates — it doesn't need the server's private key. Create one the same way as the server keystore, minus the server's own cert:
gsk8capicmd_64 -keydb -create -db client.p12 -pw 'password' -stash -type p12
gsk8capicmd_64 -cert -add -db client.p12 -stashed -label "Corporate Root CA" -file root-ca.pem -format ascii
gsk8capicmd_64 -cert -add -db client.p12 -stashed -label "Corporate Intermediate CA" -file intermediate-ca.pem -format ascii
gsk8capicmd_64 -cert -list -db client.p12 -stashed
Can the server and client share one keystore?
Technically yes — the server's own mydatabase.p12 already
contains the CA chain a client needs, so a client's conssl.cfg
could just point SSL_KEYSTORE_FILE at it directly. It's generally
not recommended anyway:
- Private-key exposure. A client only ever needs CA trust, never the server's private key. Sharing the keystore hands it out anyway.
- Separation of purpose. "
mydatabase.p12= server identity,client.p12= client trust" is easier to reason about than one file doing both jobs. - File permissions. The server keystore should stay
600 informix:informix. A shared file has to be readable by whatever else needs client trust, widening access to something that should stay locked down. - Certificate rotation. Renewing the server cert shouldn't require touching client trust at all. One shared file couples two things that don't need to change together.
- Containers. In a container or OpenShift, a client pod
needs CA trust, not the server's private key. Shipping only
client.p12keeps the private key out of places it doesn't need to exist.
Sharing is fine for a quick local/dev test; use separate server and client stores in production.
Legacy KDB clients
Older Informix/CSDK releases use a GSKit CMS keystore instead of PKCS#12:
gsk8capicmd_64 -keydb -create -db client.kdb -pw 'password' -stash
gsk8capicmd_64 -cert -add -db client.kdb -stashed -label "Corporate Root CA" -file root-ca.pem -format ascii
.kdb and .p12 are different formats, not just
different extensions — renaming one to the other does not convert it.
Testing with DB-Access
export INFORMIXDIR=/opt/ibm/informix
export PATH=$INFORMIXDIR/bin:$PATH
export INFORMIXSERVER=mydatabase_ssl
dbaccess sysmaster -
A successful connection here validates the whole chain at once — DNS, TCP, TLS, certificate trust, Informix SQLI, and authentication all have to work for this to succeed. That's also why a DB-Access failure alone doesn't tell you which layer broke; see the diagnostic sequence below for isolating it.
Java and JDBC
Java doesn't use conssl.cfg at all. The JVM uses its own
truststore (which CAs to trust) and, optionally, a
keystore (a client certificate to present, only needed for
mutual TLS). For a normal server-authentication-only JDBC connection, a
truststore is enough.
Build a JKS or PKCS#12 truststore with keytool:
# JKS
keytool -importcert -alias corporate-root -file root-ca.pem -keystore informix-truststore.jks
keytool -importcert -alias corporate-intermediate -file intermediate-ca.pem -keystore informix-truststore.jks
keytool -list -v -keystore informix-truststore.jks
# or PKCS#12
keytool -importcert -alias corporate-root -file root-ca.pem -keystore informix-truststore.p12 -storetype PKCS12
Point the JVM at it directly:
java -Djavax.net.ssl.trustStore=/opt/app/ssl/informix-truststore.jks \
-Djavax.net.ssl.trustStorePassword='password' \
-jar application.jar
Or pass it through the JDBC URL itself:
jdbc:informix-sqli://prod-db01.example.com:9089/database:
INFORMIXSERVER=mydatabase_ssl;
SSLCONNECTION=true;
SSL_TRUSTSTORE=/opt/app/ssl/informix-truststore.jks;
SSL_TRUSTSTORE_PASSWORD=password
Keep Java diagnostics separate from native-client diagnostics — a working
dbaccess connection proves the server side is fine, but Java has
its own trust mechanism and can still fail independently. Turn on handshake
tracing when it does:
-Djavax.net.debug=ssl,handshake
-Djavax.net.debug=ssl,handshake,trustmanager # more detail
-Djavax.net.debug=all # very verbose
Common Java-side errors and what they usually mean:
| Error | Usually means |
|---|---|
PKIX path building failed | Missing or untrusted CA in the truststore |
unable to find valid certification path | Missing intermediate CA in the chain |
No subject alternative DNS name matching ... | Hostname mismatch between the connection and the certificate's SANs |
protocol_version | Client/server TLS version mismatch |
The Java runtime an application actually uses can differ from your interactive
shell's java. Check the running process directly:
ps -ef | grep java
tr '\0' '\n' </proc/PID/environ | grep -i ssl
tr '\0' ' ' </proc/PID/cmdline
Diagnosing a failure, one layer at a time
The core principle: TCP connectivity, a TLS handshake, certificate trust, certificate hostname, Informix SQLI, and Informix authentication are six separate things. A working TCP connection proves nothing about TLS. A successful TLS handshake proves nothing about hostname validation. A trusted certificate can still have the wrong hostname. A successful TLS connection proves nothing about whether the account can actually authenticate. Work through them in order rather than guessing which one broke:
DNS -> TCP -> Informix listener -> onsocssl -> server keystore ->
certificate -> trust chain -> hostname validation -> client keystore ->
Informix SQLI -> authentication
1–3: environment, DNS, TCP
env | egrep 'INFORMIX|DBLANG|CLIENT_LOCALE|LD_LIBRARY'
echo "$INFORMIXDIR"; echo "$INFORMIXSERVER"; echo "$INFORMIXSQLHOSTS"
which dbaccess
A frequent, easy-to-miss failure mode: inspecting the files belonging to one
CSDK installation while a different one is actually on PATH.
getent hosts prod-db01.example.com
nc -vz prod-db01.example.com 9089
A successful nc only proves TCP connectivity — it says
nothing about whether TLS, the certificate, or hostname validation will work.
Make sure the hostname the client actually uses matches what the certificate
was issued for.
4–5: listener and sqlhosts
ss -ltnp | grep ':9089'
onstat -g ntt
Confirm the expected Informix instance, not some other process, owns that port. Then check the sqlhosts entry itself:
grep -n 'mydatabase' "${INFORMIXSQLHOSTS:-$INFORMIXDIR/etc/sqlhosts}"
A common mistake here is using onsoctcp against the TLS port, or
onsocssl against the plain port — both connect at the TCP
level and fail afterward in a way that looks like a certificate problem.
6–8: server keystore, stash, label
ls -al $INFORMIXDIR/ssl/mydatabase.*
gsk8capicmd_64 -cert -list -db $INFORMIXDIR/ssl/mydatabase.p12 -stashed
If GSKit can't read the keystore, fix that before looking anywhere near JDBC or application code. If the stash specifically is the problem:
# fails?
gsk8capicmd_64 -cert -list -db mydatabase.p12 -stashed
# but this works ->
gsk8capicmd_64 -cert -list -db mydatabase.p12 -pw 'password'
If password access works but -stashed doesn't, the stash file
itself is the problem — it's the wrong stash, belongs to a different
keystore, is reached via a wrong symlink, has the wrong filename, or has a
permissions problem. Then confirm the configured label actually matches a
personal certificate in the store:
onstat -g cfg SSL_KEYSTORE_LABEL
gsk8capicmd_64 -cert -list -db $INFORMIXDIR/ssl/mydatabase.p12 -stashed
9–11: certificate validity, SANs, chain
gsk8capicmd_64 -cert -details -db mydatabase.p12 -stashed -label mydatabase # check Not Before / Not After
gsk8capicmd_64 -cert -extract -db mydatabase.p12 -stashed -label mydatabase -target /tmp/mydatabase.pem -format ascii
openssl x509 -in /tmp/mydatabase.pem -noout -ext subjectAltName
openssl verify -CAfile root-ca.pem -untrusted intermediate-ca.pem /tmp/mydatabase.pem
Expect /tmp/mydatabase.pem: OK from the last command.
To check whether a certificate is about to expire rather than just whether it already has:
openssl x509 -in /tmp/mydatabase.pem -checkend 2592000 -noout # 30 days
openssl x509 -in /tmp/mydatabase.pem -checkend 604800 -noout # 7 days
12–13: what's actually being presented on the wire
The certificate sitting in mydatabase.p12 is not necessarily the
certificate the live endpoint is presenting — the wrong Informix
instance, a stale container image, an old mounted secret, the wrong NodePort, a
load balancer or reverse proxy in front, the wrong server, wrong DNS, or an old
process that never restarted can all cause the two to diverge. Always check the
live endpoint directly when a difficult failure doesn't make sense against the
file on disk:
openssl s_client -connect prod-db01.example.com:9089 -servername prod-db01.example.com -showcerts
Or pull just the subject/issuer/dates:
openssl s_client -connect prod-db01.example.com:9089 -servername prod-db01.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -serial -dates
Comparing SHA-256 fingerprints is the fastest way to confirm the installed and live certificates actually match:
# installed
openssl x509 -in /tmp/mydatabase.pem -noout -fingerprint -sha256
# live
openssl s_client -connect prod-db01.example.com:9089 -servername prod-db01.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha256
14–16: client truststore, conssl.cfg, symlinks
gsk8capicmd_64 -cert -list -db client.p12 -stashed
gsk8capicmd_64 -cert -details -db client.p12 -stashed -label "Corporate Root CA"
cat $INFORMIXDIR/etc/conssl.cfg
ls -al /opt/ibm/informix/etc/client.*
If the client keystore files are symlinked, always resolve and inspect the real target, not the link:
readlink -f /opt/ibm/informix/etc/client.p12
readlink -f /opt/ibm/informix/etc/client.sth
17: strace, when it's unclear which file is actually being opened
strace -f -o /tmp/dbaccess.strace dbaccess sysmaster -
grep -E 'conssl|client\.p12|client\.kdb|client\.sth|sqlhosts' /tmp/dbaccess.strace
This answers exactly which sqlhosts file, conssl.cfg,
and keystore were actually opened — and whether one of them was missing
or denied by permissions, rather than just wrong.
18: the Informix message log
onstat -m
onstat -g cfg MSGPATH
grep -iE 'ssl|tls|gsk|certificate|handshake|socket' /path/to/online.log
tail -f /path/to/online.log # while reproducing the connection attempt
A GSKit-layer failure shows up in the log as Informix error -28014
("Secure Sockets Layer error") with a GSK_ERROR_* name attached,
for example:
err = -28014: oserr = 107: errstr = GSK_ERROR_IO: Secure Sockets Layer error: GSK_ERROR_IO.
-28014 is just the generic wrapper — the actual cause is
always in that GSK_ERROR_* name. See the next section for what
each one means.
Packet capture, as a last resort
ss -tnp | grep 9089
tcpdump -nn -s0 -i any port 9089
tcpdump -nn -s0 -i any port 9089 -w /tmp/informix-tls.pcap
This can distinguish a TCP reset, a TLS alert, a timeout, or which side actually closed the connection — useful when everything above looks correct and the connection still fails.
Reading the GSKit error code
Once a GSKit-layer failure is confirmed, the GSK_ERROR_* name in
the log line is the actual diagnosis — -28014 itself never
changes no matter which one fires. These five account for most real cases seen
against Informix, each confirmed against a real IBM APAR pairing it with
-28014 specifically:
| GSKit name | gsk rc | Meaning |
|---|---|---|
GSK_ERROR_BAD_DATE | 401 | Certificate expired, or not yet valid — see certificate expiration above |
GSK_ERROR_IO | 406 | I/O error during SSL processing — check the accompanying oserr/errno, not the certificate |
GSK_ERROR_BAD_MESSAGE | 410 | Peer sent a badly formatted or unrecognized SSL message — usually a protocol/cipher mismatch |
GSK_ERROR_BAD_PEER | 415 | Peer's certificate or protocol wasn't recognized — version/cipher mismatch, or a GSKit version skew between the two ends |
GSK_ERROR_SOCKET_CLOSED | 420 | Socket closed unexpectedly mid-handshake — usually a symptom of one of the above, not a root cause on its own |
GSK_ERROR_BAD_DATE and GSK_ERROR_BAD_PEER/
GSK_ERROR_BAD_MESSAGE point back at the certificate/chain/protocol
checks earlier in this article; GSK_ERROR_IO and
GSK_ERROR_SOCKET_CLOSED point away from the certificate entirely and
toward the network layer — worth knowing before spending time re-checking
a keystore that was never the problem. GSK_ERROR_SOCKET_CLOSED in
particular has been reported hanging new SSL connections under load (roughly
200+ concurrent) while plain TCP kept working — a capacity/stress
condition, not a misconfiguration.
Common failure scenarios
TCP fails
Don't touch certificates yet. Check DNS, the firewall, routing, whether the listener is actually up, port mapping/NodePort, and that the client is using the right hostname at all.
TCP passes, TLS fails
Check the certificate, keystore, stash, SSL_KEYSTORE_LABEL, TLS version, certificate chain, and that sqlhosts/NETTYPE agree on protocol.
Hostname mismatch
Client connects to prod-db01.example.com; the certificate's only SAN is DNS:dev-db01.example.com. The trust chain can be entirely valid while the identity is still wrong. Fix the certificate's SAN, or connect using a name the certificate actually covers.
Certificate expired
gsk8capicmd_64 -cert -details -db mydatabase.p12 -stashed -label mydatabase
Check Not After.
Missing intermediate CA
Chain is Root CA → Intermediate CA → server certificate. If the client doesn't have the intermediate, validation can fail even though the root CA is trusted.
Wrong SSL_KEYSTORE_LABEL
SSL_KEYSTORE_LABEL mydatabase configured, but the personal certificate in the keystore is actually labeled server-cert. Fix either side so they match.
Stash problem
-stashed fails but -pw 'password' works — the
keystore itself is fine, the stash file (mydatabase.sth) is the
problem: wrong stash, belongs to another keystore, wrong symlink target, wrong
filename, or a permissions issue.
Diagnostic collection checklist
What to gather up front, before going deeper — grouped by where it runs:
Server
onstat -V
onstat -g cfg DBSERVERNAME
onstat -g cfg SSL_KEYSTORE_LABEL
onstat -g cfg TLS_VERSION
onstat -g cfg NETTYPE
grep -E 'onsoctcp|onsocssl' $INFORMIXDIR/etc/sqlhosts
ls -al $INFORMIXDIR/ssl
gsk8capicmd_64 -cert -list -db $INFORMIXDIR/ssl/mydatabase.p12 -stashed
gsk8capicmd_64 -cert -details -db $INFORMIXDIR/ssl/mydatabase.p12 -stashed -label mydatabase
Client
dbaccess -V
echo "$INFORMIXDIR"; echo "$INFORMIXSERVER"; echo "$INFORMIXSQLHOSTS"
cat $INFORMIXDIR/etc/conssl.cfg
cat "${INFORMIXSQLHOSTS:-$INFORMIXDIR/etc/sqlhosts}"
ls -al $INFORMIXDIR/etc/client*
gsk8capicmd_64 -cert -list -db $INFORMIXDIR/etc/client.p12 -stashed
Network
getent hosts prod-db01.example.com
nc -vz prod-db01.example.com 9089
openssl s_client -connect prod-db01.example.com:9089 -servername prod-db01.example.com -showcerts
Java
java -version
keytool -list -v -keystore informix-truststore.jks
# then run the app with -Djavax.net.debug=ssl,handshake,trustmanager
Containers and OpenShift
In a containerized deployment, the certificate reaches the running process
through a chain of its own: a Secret is mounted as a volume into the
container's filesystem, where it lands as mydatabase.p12 and gets
read by Informix. Always inspect the certificate inside the running
container, not just the source Secret:
ls -al /opt/ibm/informix/ssl
sha256sum /opt/ibm/informix/ssl/mydatabase.p12
gsk8capicmd_64 -cert -list -db /opt/ibm/informix/ssl/mydatabase.p12 -stashed
If Informix listens on 9089 inside the container and OpenShift
exposes it externally as a NodePort (say 31001), the path is
client → node.example.com:31001 → OpenShift NodePort
→ pod :9089 → Informix. Changing the exposed TCP port
doesn't change the TLS identity being presented; changing the hostname the
client connects to might, if that hostname isn't covered by the certificate's
SANs.
Why this layout, not some other one
- PKCS#12 over KDB for new installs. It's a standard format
GSKit, OpenSSL, Java, and
keytoolall support natively — easier to inspect and integrate than a proprietary GSKit CMS store. - Separate server and client stores. The server needs its certificate, private key, and the CA chain; the client only ever needs CA trust. Keeping them separate avoids handing the private key to anything that doesn't need it.
- Validate the live endpoint, not just the file. Checking the PKCS#12 file only proves what's on disk — it says nothing about what's actually being served. The live TLS connection is the authority on what a client actually sees.
- DB-Access as the native end-to-end test. OpenSSL validates TLS alone; DB-Access validates TLS and Informix SQLI and authentication together — the two tools answer different questions.
- Keep Java diagnostics separate. The JVM has its own trust mechanism. A clean native DB-Access connection doesn't guarantee a JVM truststore is configured correctly, or vice versa.
Key Takeaways
- TCP, TLS handshake, certificate trust, certificate hostname, Informix SQLI, and Informix authentication are six separate things that each have to pass on their own — work through them in that order instead of guessing.
- The server keystore is selected by
DBSERVERNAME;SSL_KEYSTORE_LABELthen picks which personal certificate inside it to present. Both have to line up with what's actually in the keystore. - The certificate on disk and the certificate a live endpoint presents can
differ — wrong instance, stale container image, old Secret, a proxy in
front. When a failure doesn't make sense, check
openssl s_clientagainst the real endpoint before anything else. - Java doesn't read
conssl.cfg. It has its own truststore/keystore mechanism and needs its own, separate diagnosis.