TT Lab
Get started
Learn Learning paths Courses

Building an EAI Middleware Layer

Different Certificates, Different Formats — Stand Up the External Gateway

Continue in TT Lab

Goal

Build an external gateway that connects to two external institutions over mutual TLS. Get certificates issued per institution, keep institution profiles as data, translate to the institution's specification and send, receive only from allowed addresses, and monitor certificate expiry.

Why it matters

External institutions do not adapt to our standard. If you do not confine the differences in specification, certificate and allowed address in one place, the external gateway, you end up fixing the inner systems every time an institution is added. And the regulars of external outages — certificate expiry, certificate mix-up, and misunderstanding the length criterion — can all be prevented or noticed early in this layer.

Steps

  1. Create the two institutions' CAs with python3 /opt/lab/fixtures/eaimw/fep/pki.py init /root/eaimw/fep/partners. For each institution, create a private key and a CSR (with O=LabHub Bank in the subject and labhub in the CN), and get them signed with python3 /opt/lab/fixtures/eaimw/fep/pki.py sign A <CSR> /root/eaimw/fep/a.crt and … sign B <CSR> /root/eaimw/fep/b.crt. The keys are /root/eaimw/fep/a.key and /root/eaimw/fep/b.key.
  2. Look at the specification /opt/lab/fixtures/eaimw/fep/ORGS.md and write /root/eaimw/fep/orgs.json. The top-level keys are institution codes (201 and 301), and the values are host, port, format (fixed/json), encoding (euc_kr/utf-8), len_includes_self (true for A, null for B), ca, cert, key (absolute paths) and allow_ips (a list).
  3. Start the institution A server (nohup python3 /opt/lab/fixtures/eaimw/fep/org_server.py A > /root/eaimw/fep/a.out 2>&1 &, 9441), give a.crt, a.key and the institution A CA to openssl s_client, send a single line PING, and save the PONG <지문> (PONG followed by the fingerprint) you receive to /root/eaimw/fep/a-ping.txt.
  4. Put b.crt and b.key into the same command and try to connect to institution A, and save standard output and standard error together (2>&1) to /root/eaimw/fep/wrong.txt. The handshake must be rejected.
  5. /root/eaimw/fep/fepgw.py send <기관코드> <요청JSON파일> (institution code, request JSON file): find the institution in the profile (FEP_ORGS, default /root/eaimw/fep/orgs.json), connect with mutual TLS using that institution's certificate, and verify the institution server's certificate with that institution's CA. If the environment variable FEP_PORT_<기관코드> (institution code) exists, use it for the port. For 201, send the request {guid,date,amount,acct,name} in the A specification's fixed-length format (the length includes itself) and output the result as one line of JSON {"org","rspCode","guid","realName","clientFp"}; for 301, send {guid,custId} with POST /v1/credit-check and output {"org","rspCode","guid","grade","clientFp"}. Convert response codes to LH-STD (00→0000, 14→B202, 30→E102, anything else E500).
  6. /root/eaimw/fep/inbound.py --port <P>: receive connections made by external institutions (plaintext TCP, A specification). If the source address is not in any institution's allow_ips, disconnect without reading and, in FEP_LOG (default /root/eaimw/fep/inbound.log), leave one line DENY <주소> (DENY followed by the address). For an allowed address, return an A specification response (message type 0210, the same transaction unique number, response code 00, the real-name field 20 spaces, the fingerprint field 64 - characters, the length including itself).
  7. /root/eaimw/fep/certcheck.py --warn-days <N>: for each institution in the profile, print one line <기관코드> <만료일 YYYY-MM-DD> <남은일수> <OK|WARN> (institution code, expiry date, days remaining, OK or WARN), in institution code order. If the remaining days are fewer than N, it is WARN, and if there is even one WARN, it ends with a non-zero code.

Notes

Get a certificate issued per institution

Create the two institution CAs, create a key and CSR for each institution and get them signed, and place a.crt/a.key and b.crt/b.key.

The private key stays on our side and only the CSR is sent. Institution A's must be signed by A and institution B's by B — if one CA signs both, the institution cannot reject the other's certificate.

Institution profiles as data

Look at ORGS.md and write the two institutions' address, format, encoding, length criterion, certificates and allowed IPs into /root/eaimw/fep/orgs.json.

So that no institution name appears in the code, move all the values that differ by institution into this file. The length criterion does not apply to JSON institutions, so it is null.

Connect to institution A over mutual TLS

Start the institution A server, send PING with a.crt, and save PONG to /root/eaimw/fep/a-ping.txt.

Give openssl s_client both -cert and -key (my identity) and -CAfile (the institution CA, to verify the server). With -quiet, only the response body remains. The fingerprint is my certificate as the institution saw it.

Swapping certificates gets you rejected

Try to connect to institution A with b.crt and b.key, and save the output and errors together to /root/eaimw/fep/wrong.txt.

Change only the certificate and key in the step 3 command. The rejection reason (a TLS alert) comes out on standard error, so save it together with 2>&1.

Choose per institution and send

/root/eaimw/fep/fepgw.py send sends with each institution's certificate and specification and outputs one line of JSON with the result normalized to LH-STD codes.

Look at format in the profile and pick an adapter. For A, after building the whole body, use len(body)+4 as the length (including itself). Server verification is create_default_context(cafile=institution CA), and my identity is load_cert_chain.

The inbound side — only from allowed addresses

/root/eaimw/fep/inbound.py --port P accepts only connections from the profile's allow_ips and answers in the A specification, and disconnects the rest and records DENY.

The socketserver handler's self.client_address[0] is the source address. The allow list is the allow_ips of all institution profiles combined. When rejecting, do not read; return right away.

Know about expiry first

/root/eaimw/fep/certcheck.py --warn-days N prints each institution's expiry date, days remaining and OK/WARN, and ends with a non-zero code if there is a WARN.

Read the certificate's notAfter and count the difference from now (UTC) in days. Some institutions take weeks to replace, so set the threshold generously — monitoring tools notice through the exit code.