DEV Community

Jay
Jay

Posted on

# How I Encrypt and Use Credentials on IBM i

How I Encrypt and Use Credentials on IBM i

Because the password probably shouldn't be in the CL source


I'm going to say something a little uncomfortable.

If you work on IBM i and your shop has been around for a while, there is almost certainly a password sitting in clear text in a source member right now. Probably several. Probably an FTP password, maybe a database connection string, maybe a vendor API key. Maybe in a data area somewhere that doesn't have its authority locked down. Maybe in a CL that anyone with read authority on the library can display.

Not because your team is careless. It's just how things were done, and nobody has had the time to go back and fix it.

This post is the "how I finally fixed it" — specifically, how I'm storing and using credentials in a real production framework on IBM i, using the platform's own cryptographic APIs, without adding any new technology to the stack.


Why this is harder than it looks

The first time you sit down to solve "encrypt the password," a handful of bad patterns suggest themselves:

  • Base64 or a simple XOR — not encryption. Someone with read access can decode it in their head.
  • "I'll just hash it" — hashing is one-way. You can't log into FTP with a hash. You need the plaintext at the moment you use it.
  • Third-party encryption library — now you have a dependency, a license question, and a library running in your service program. You don't need any of that.
  • Hardcode a key in source — now the key is in the source. Same problem, one level deeper.

What you actually need is:

  • Strong encryption at rest — actual AES, not a scheme you made up.
  • A key that lives somewhere separate from both the ciphertext and the source — and with restrictive authority.
  • Decryption only at the moment of use — the plaintext exists in memory for as briefly as possible.
  • The plaintext never goes anywhere else — not a log, not a spool, not an error message, not the system operator queue, not an audit record.

IBM i has everything you need for all of this, and has for years. You just have to wire it up.


The tools IBM gave us

IBM i has a set of cryptographic APIs. The two you care about are the encrypt and decrypt primitives — they let you encrypt a block of data with a specified algorithm and key, and decrypt it back. They support the standard modern algorithms (AES-128, AES-192, AES-256) in the standard modes (ECB, CBC, OFB, CFB, CTR). They're callable from RPGLE. They've been part of the platform for nearly two decades. They're fine.

I'm using AES-256-CBC. AES-256 because there's no reason not to in 2026. CBC because it's straightforward, well-understood, and the per-encryption IV property is exactly what we want for "each stored password has a different ciphertext even if two users chose the same password."

You don't need to write the cipher yourself. You don't want to write the cipher yourself. The built-in APIs are the adult in the room.


Where the key lives

This is where most implementations get it wrong, so let me be deliberate about it.

The key is not in the source code. It's not in a physical file. It's not in the same library as the configuration table it encrypts. It's in an IBM i data area, authority-locked down.

Three things are happening when you set it up:

  1. A 32-byte character data area. AES-256 needs a 256-bit key, which is 32 bytes. Nothing fancy.
  2. Exclusive authority to public. Nobody gets to read this by default. Not end users, not developers, nothing.
  3. Explicit grant to the service program's user profile. The encryption service program runs under a specific profile that has read access to the data area. Nothing else does.

If your service program uses program-adopted authority (the owner having read access to the key data area), you get the same effect — the service program can read the key, but the calling program and the calling user still can't.

This is the kind of thing IBM i is genuinely good at, and we forget to use. Object-level authority on a single data area is a more rigorous access control than most cloud secret managers offer out of the box. It just looks unfamiliar because we're used to reading about third-party vault products instead.


The encryption service program

The heart of this is a tiny service program. Two exported procedures. That's the whole API.

  • Encrypt — takes clear-text (say, up to 64 characters, which is plenty for a password) and returns a larger field containing a 16-byte IV followed by the ciphertext.
  • Decrypt — takes the stored field back and returns the clear-text.

That's the whole public surface area. Everything else is an implementation detail of those two procedures.


Inside Encrypt (sketch)

The real file is a couple hundred lines. The shape is:

dcl-proc Encrypt export;
  dcl-pi *n varchar(256);
    plaintext varchar(64) const;
  end-pi;

  // 1. Read the key from the data area
  //    (only this procedure, under its adopted authority,
  //    is allowed to do this)

  // 2. Generate a fresh IV (16 bytes, random)
  //    using the platform PRNG

  // 3. Build the algorithm descriptor for AES-256-CBC
  //    with this IV

  // 4. Build the key descriptor for our 32-byte key

  // 5. Call the encrypt primitive with plaintext,
  //    algorithm, key, result buffer

  // 6. Concatenate IV + ciphertext and return

end-proc;
Enter fullscreen mode Exit fullscreen mode

Two things to note:

The IV is fresh every call. A new IV per encryption means that the same password encrypted twice produces two different ciphertexts. This is not optional; it's the security property CBC is trying to give you.

The IV is stored alongside the ciphertext, not hidden. The IV isn't secret — it just has to be unpredictable. Prepending it to the ciphertext means decryption doesn't need to know anything except the 32-byte key and the stored field itself.

Decrypt is the mirror image: slice off the first 16 bytes as the IV, feed the rest into the decrypt primitive with the same key and algorithm, return the plaintext.


Using it safely at the call site

This is the part that matters more than the crypto.

Here's how the execution program — the thing that actually performs the FTP transfer — uses a stored credential:

// 1. Read the encrypted password from the config table

// 2. Decrypt — plaintext now exists in a local field

// 3. Use it immediately — the login call is the only thing
//    that sees it

// 4. Clear it from memory the instant we're done
Enter fullscreen mode Exit fullscreen mode

Four steps, in this order, no exceptions. The plaintext exists in memory for the duration of the login call and is cleared the moment login returns — whether the login succeeded or failed. The clear-text field never leaves this procedure.

What's deliberately missing:

  • No display of the plaintext. Obviously.
  • No log line that includes the password. Also obviously. But I've seen this in the wild.
  • No copy into a global field for "later use." There is no "later use."
  • No error message that includes the password. If the login fails, the error is "login failed for user X." That's it. The password is not the problem; the problem is whatever the server said back.
  • No audit record with the plaintext. The audit table logs changes to the encrypted field as masked asterisks. Always. If you ever need to prove that a password changed for compliance, you can see the timestamp and the user. You cannot see the value. That is the correct behavior.

If I could do one thing to make people's IBM i shops more secure, it would be: get everyone to stop logging the password "just for debugging, I'll remove it later." You won't. It'll be in a spool file for three years.


What about key rotation?

You need a plan for this before you deploy, not after.

My approach:

  1. Generate a new 32-byte key. Store it somewhere outside IBM i for the duration of the rotation (a password manager, a sealed envelope — whatever your compliance model allows).
  2. For each row in the config table: read the stored encrypted field, decrypt with the old key, re-encrypt with the new key, write back.
  3. Update the data area to the new value.
  4. Destroy the external copy of the new key.

This rotation is the kind of job that lives in a one-shot program that takes the old key and the new key as parameters and runs once. You throw it away after rotation. You don't leave it running anywhere.

There's a more elegant version of this where you have a key ID column next to each ciphertext and can support multiple keys in flight during a rotation window. For most shops, the simple "rotate all at once" pattern is fine — it takes a couple of seconds even with thousands of rows.


What else I'd watch for

A few things I've seen people get wrong, even with the encryption piece done correctly:

The password in a job parameter. If you submit a job with a clear-text password as one of its parameters, that parameter is visible in the job's submitted-jobs view and in the job log. Never pass a plaintext password as a parameter.

The password in a temporary physical file. "I'll just stage it in a work file for a second." No. Work files persist until they're cleared. If the job crashes, they persist longer. Don't.

The password in a user space. User spaces are visible with the right authority. If you wouldn't write the password to a physical file, don't write it to a user space.

Adopted authority for the wrong thing. If your service program adopts authority to read the key data area, make sure the programs that call it don't also adopt that authority. The whole point is that the key is only visible to one narrow thing.

Forgetting the clear. In RPG, local fields go out of scope when the procedure exits, and their memory will eventually be reused. "Eventually" is the issue — a failed login could leave the plaintext sitting in the activation group's memory until something else overwrites it. An explicit clear on the field as the last thing you do is cheap insurance.


Why this is worth doing even if nobody asks for it

No regulator is auditing your IBM i shop for clear-text FTP passwords. No CISO is going to find them unless they look. No application scanner is going to flag them because nothing runs against RPG source.

This is the kind of problem you either fix because you care, or you don't fix at all.

Here's the honest argument for doing it: the day someone does ask — because of a breach, an audit, a new vendor, a merger — the fix will take an afternoon if you've already built the pattern, and it will take months if you haven't. The encryption service program in my current framework is a couple hundred lines of RPG. The data area is one command to create. The authority lockdown is one command. Every program that uses a credential already goes through the Encrypt / Decrypt procedures. If I need to rotate the key, it's one program and fifteen minutes.

None of this is clever. None of it is new. IBM gave us these cryptographic APIs two decades ago and they've been sitting there, waiting.


Wrapping up

If you take one thing from this post, take this: passwords on IBM i should live in an encrypted field in a config table, encrypted with an AES-256 key stored in an authority-locked data area, decrypted by a small service program only at the moment of use, cleared from memory immediately after, and never written anywhere else. That's the whole pattern.

You don't need a vault. You don't need a new tool. You don't need to learn anything outside the platform. The cryptographic APIs are right there.

A framework I've been working on — an FTP integration layer I rebuilt from a pile of ad-hoc scripts — uses exactly this pattern for every credential it stores. It's a couple hundred lines of RPG and a data area. It's the cheapest, most durable security improvement I've made to an IBM i codebase in years. (I'll write that one up in a follow-up post.)

If you do this for one program, you'll end up doing it for more. And the next time someone opens a CL and sees a password in plain text, you'll notice, and you'll fix it.

That's the real goal. Not the crypto. The habit.


Jaya Krushna Mohapatra is a Warehouse Management Systems Architect focused on enterprise integrations, IBM i modernization, and scalable backend systems.

Top comments (0)