Java API Programming Guide
© Pi Soft, 2018-2026 · Tricryption Engine 8.1
The Java agent is the tech.pisoft.teagent package of the TEAdmin jar (TEAdmin-8.1.0.jar, built by the product's te_java_teadmin target into build/Java). It is compiled for Java 25 (class-file version 69), so it needs a Java 25 or later runtime. This page is a guide only: the Java classes are documented in the separate Java API reference, generated from each class's own documentation comments, which are the reference for its methods.
General procedure
- Create an authentication context for your credential type and set its fields (see Authentication contexts).
- Create a TLS connection, normally with
TEAgentConnectionFactory.getConnection(TEConnectionType.SECURE_SOCKET), and callopen(server, port, context). The connection authenticates as part ofopen; a refusal is thrown as aTEAgentException(a Key Server refusal as its subclassTEServerException). - Use the connection: for example
TEAgent.encryptBuffer(conn, data, hl)andTEAgent.decryptBuffer(conn, cipher, hl[0]), or the TEAdmin administration classes. - Call
close()on the connection.
getLoginID() on an open connection returns the principal id the Key Server established for the session.
Connections
| connection class | how it is obtained | used for |
|---|---|---|
TEAgentSSLAuthenticConnection | TEConnectionType.SECURE_SOCKET | every context below except the two that present a client certificate at TLS |
TEAgentSSLMConnection | TEConnectionType.SSLM_SOCKET | SSLMAuthenticationContext and DelegatedAuthenticationContext, whose credential is the client certificate the TLS handshake presents |
Authentication contexts
Every context extends AuthenticationContext; the registry of Key Server modules is TEAuthenticationType (USERANDPASSWORD, LDAP, COOKIE, SSLM, DELEGATED, GSSAPI).
| context | Key Server module | credential |
|---|---|---|
UPAuthenticationContext | SRPAuthentication | user name and password (setUser, setPasswd); SRP-6a/SHA-384 |
LDAPAuthenticationContext | LDAPAuthentication | directory user and password (setUser, setPasswd) |
CookieAuthenticationContext | TokenAuthentication | a session token from an authenticated connection (setCookie) |
FileX509Context | X509Authentication | a PKCS#12 file holding a certificate and its RSA or EC key (open(path, password)); the signature is bound to the connection |
SSLMAuthenticationContext | mutual TLS (not a module) | a client certificate presented at the handshake (setSecret); setServerAnchor names the Key Server's CA |
DelegatedAuthenticationContext | DelegatedAuthentication | the delegator's client certificate (setSecret) and ONE target (setX509Target or setXAuthTarget) |
GSSAPIAuthenticationContext | GSSAPIAuthentication | a Kerberos ticket the caller already holds — see Kerberos login |
Kerberos login
GSSAPIAuthenticationContext logs in with Kerberos through the Key Server's GSSAPIAuthentication module (AM10). The Key Server must have the module enabled; the operator steps are in the repository's docs/KERBEROS_AM10.md.
Settings.
-
setServicePrincipal(String)— required: the Key Server's acceptor principal, the name its keytab holds, e.g.te/ks.example.com@EXAMPLE.COM. -
The credential, one of:
setCredentialCache(String)— a credential cache name (FILE:/path,KCM:, ...); with neither this nor a Subject set, the default cache is used;setSubject(javax.security.auth.Subject)— a JAAS Subject holding a Kerberos ticket-granting ticket (asKrb5LoginModuleleaves it). The ticket is written, for the length of the login only, to a private owner-only cache file, which is removed afterwards. Setting one clears the other.
-
setServerAnchorFile(String)orsetServerAnchor(InputStream)— required: the certificate the connection trusts the Key Server by (PEM or DER, one or more; normally the Key Server's CA; a certificate that is not self-signed pins it). -
setInitiatorPath(String)— where thete_gss_initiatorhelper is (below). -
setKrb5Config(String)— the Kerberos profile the helper reads (itsKRB5_CONFIG); unset, the helper inherits this process's environment.
The helper. The Kerberos half of the login runs in te_gss_initiator, a native executable built with the product (target te_gss_initiator, the same pinned MIT Kerberos as the C and C++ agents). JGSS is not used: its Kerberos provider cannot declare channel-binding awareness, which the Key Server requires. The context finds the helper, in order, at the path given to setInitiatorPath, in the system property tech.pisoft.teagent.gssInitiator, in the environment variable TE_GSS_INITIATOR, and finally as te_gss_initiator on PATH. It is a product of the C++ build (build-cmake/<preset>/te_gss_initiator); copy it to the client host and name it or put it on PATH.
The binding is carried for you. The context reads the connection's own RFC 9266 channel binding (tls-exporter), hands it to the helper, and relays each Kerberos token; mutual authentication is requested and confirmed. The Kerberos profile the helper reads must declare client_aware_channel_bindings = true under [libdefaults], or the Key Server refuses the login as unbound.
The anchor is checked twice. Opening a TEAgentSSLAuthenticConnection with this context verifies the Key Server's certificate against the anchor at the handshake, before any protocol byte; and the exchange verifies the presented chain again before its first token, whatever connection it is given.
import tech.pisoft.teagent.GSSAPIAuthenticationContext;
import tech.pisoft.teagent.TEAgent;
import tech.pisoft.teagent.TEAgentConnection;
import tech.pisoft.teagent.TEAgentConnectionFactory;
import tech.pisoft.teagent.TEAgentException;
import tech.pisoft.teagent.TEConnectionType;
public class Java_Connect_Kerberos {
public static void main(String[] args) {
try {
GSSAPIAuthenticationContext ctx = GSSAPIAuthenticationContext.getInstance();
ctx.setServicePrincipal("te/ks.example.com@EXAMPLE.COM");
ctx.setServerAnchorFile("/etc/te/ks-ca.pem"); // required
ctx.setInitiatorPath("/opt/te/bin/te_gss_initiator");
// no setCredentialCache: the default credential cache
TEAgentConnection conn = TEAgentConnectionFactory.getConnection(TEConnectionType.SECURE_SOCKET);
conn.open("ks.example.com", 8888, ctx);
System.out.println("Login ID: " + conn.getLoginID());
byte[][] hl = new byte[1][];
byte[] cipher = TEAgent.encryptBuffer(conn, "hello".getBytes("UTF-8"), hl);
byte[] plain = TEAgent.decryptBuffer(conn, cipher, hl[0]);
conn.close();
} catch (TEAgentException e) {
e.printStackTrace();
} catch (java.io.UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
Refusals are thrown as TEAgentException:
- before anything is dialed —
Kerberos: no key server anchor set (setServerAnchor) -- a Kerberos login names the key server it trusts; - at the handshake or before the first token —
the key server's certificate did not verify: <reason>; - from the Key Server, as a
TEServerExceptionnaming the check — for exampleGSSAPI: the exchange is not bound to this connection (GSS_C_CHANNEL_BOUND_FLAG not set)(the profile setting above),GSSAPI: channel binding mismatch (GSS_S_BAD_BINDINGS) -- the exchange was not bound to this connection(a relay in the path),GSSAPI: the security context was not established(a ticket for another service, or none), or a directory-mapping refusal. The operator guide lists every one and its fix. - from the helper — a message beginning
GSSAPI initiator:, for example when the helper is not found or reports an error of its own (no ticket in the cache, an unreachable KDC).
Windows. te_gss_initiator.exe needs the MIT Kerberos package's gssapi64.dll, krb5_64.dll, comerr64.dll and k5sprt64.dll, and the product's libcrypto-3-x64.dll, on its PATH.
Deriving a cipher key
TEKey.deriveKey(type, hiddenLink, nonce) (with a TEKey attached to an open connection by attachConnection) returns a TECipher holding a key and IV for type (default TECipherAlgorithmType.TE_AES_256_CBC), derived ON THE KEY SERVER from the key the hidden link names. The derivation is NIST SP 800-108 KBKDF in counter mode with HMAC-SHA-256 as the PRF: the named key is the KDF key, the nonce is the Context, a fixed product label is the Label, and the output length is the cipher's key length plus its IV length. The named key never leaves the Key Server; only the derived bytes do. The same key, nonce and cipher always give the same key and IV, and a different nonce gives a different one, so TEAgent.encryptBuffer(cipher, data) and TEAgent.decryptBuffer(cipher, data) round-trip across two derivations.
- An empty or
nullhidden link makes the Key Server create a key, exactly as an encrypt does;cipher.getHiddenLink()returns its link for the next derivation. - The nonce must be at least 48 bytes (shorter is refused before any request);
nulluses the agent's default. - The caller needs a role granting the
Derive Keyoperation (the stockEncryptorrole does) and an entry on the key's access control list. - A key derived by the earlier construction (which encrypted the nonce) is not reproduced: derive again, and re-protect what the old key protected.
The PISOFTJCE provider
tech.pisoft.security.JniProvider (provider name PISOFTJCE, in the PiSoftOnlineJce / PiSoftOfflineJce jars) runs its algorithms in the product's native OpenSSL through the pisoftjce library, which must be on java.library.path; launch with --enable-native-access=ALL-UNNAMED.
| service | algorithm (aliases) | notes |
|---|---|---|
Cipher | AES | CTR, GCM, CBC; NoPadding or PKCS5Padding |
KEM | ML-KEM (ML-KEM-512, ML-KEM-768, ML-KEM-1024) | FIPS 203; the set comes from the key; 32-byte shared secret |
Signature | ML-DSA (ML-DSA-44, ML-DSA-65, ML-DSA-87) | FIPS 204, pure, empty context |
KeyPairGenerator | ML-KEM, ML-DSA | initialize(new NamedParameterSpec("ML-KEM-1024")); default ML-KEM-768 / ML-DSA-65 |
KeyFactory | ML-KEM, ML-DSA | X509EncodedKeySpec (public) and PKCS8EncodedKeySpec (private) |
Public keys interoperate with the JDK's own SunJCE and SUN through their X.509 encodings in both directions. PISOFTJCE accepts the JDK's PKCS#8 private keys; JDK 25 does not accept the PKCS#8 form PISOFTJCE writes, so move private keys between the two only from the JDK side.
FIPS mode. Start the JVM with -Dtech.pisoft.security.FipsMode=true. The property is read once, when PISOFTJCE first loads its native library, and the validated OpenSSL FIPS provider 3.1.2 is then loaded from the ossl-modules directory installed beside the pisoftjce library (fips.so and fipsmodule.cnf; fips.dll on Windows) — ship that directory unmodified. The switch is fail-closed: when FIPS mode cannot be honoured no PISOFTJCE class loads, and the error names the cause (PISOFTJCE: FIPS mode was requested (tech.pisoft.security.FipsMode=true) and cannot be honoured: ...). In FIPS mode AES runs in the validated module, and every ML-KEM and ML-DSA operation throws a ProviderException naming FIPS mode, because the validated module has neither. Setting the property after PISOFTJCE has loaded is refused by name on every operation rather than ignored.
java.security.Provider p = new tech.pisoft.security.JniProvider();
java.security.KeyPairGenerator g = java.security.KeyPairGenerator.getInstance("ML-KEM", p);
g.initialize(new java.security.spec.NamedParameterSpec("ML-KEM-768"));
java.security.KeyPair kp = g.generateKeyPair();
javax.crypto.KEM kem = javax.crypto.KEM.getInstance("ML-KEM", p);
javax.crypto.KEM.Encapsulated e = kem.newEncapsulator(kp.getPublic()).encapsulate();
javax.crypto.SecretKey k = kem.newDecapsulator(kp.getPrivate()).decapsulate(e.encapsulation());
Not available on JDK 25. Its JSSE negotiates no hybrid post-quantum key exchange (the product's native TLS does, in default mode), and jarsigner cannot sign with an ML-DSA key.