Using GnuPG for PGP on i

Q: I need a way to use PGP encryption on IBM i. I've found that the pgp tool can be used in PASE, but due to the license on that tool, my company can't use it. The GNU Privacy Guard (GnuPG) appears to be the most common tool out there, but I can't find it compiled for IBM i! Can you help me get it working?

A: Yes, I can. Honestly, I've received this question a bunch of times over the years, but I never really had a chance to look into it. The question resurfaced (for perhaps the tenth time) in the System iNetwork forums last week. On Tuesday, I finally had a chance to look into it. I managed to get the current version of GnuPG working on IBM i under PASE.

This article tells you where to download GnuPG for PASE and gives you a quick introduction to how to install and use it.


Because I built this tool under PASE on IBM i 5.4, I cannot promise that it will work on earlier releases. However, it should work on 5.4 and later. (However, if you compile it yourself, you may be able to get it to work on older releases.)

You must have Portable Application Solutions Environment (PASE) installed. This is a no-charge component of IBM i. It's 57xx-SS1, option 33.

I also recommend QShell, which is 57xx-SS1, option 30.

The PASE (Unix) Directory Structure

I learned Unix on the FreeBSD platform. On FreeBSD, they always take care to distinguish the difference between tools that are installed as part of the operating system and tools that have been added on separately. I find this philosophy very useful, because at upgrade time, I can wipe clean the folders containing the OS components, then reinstall them, without having to worry about wiping out any custom-installed software.

Likewise, if I want to remove all custom software and return to a fresh install, I can just wipe out the stuff that has been added later.

In FreeBSD, the directory structure looks like this:

/bin Programs ("binaries") that must always be available, even when the system is in "restricted state" to do backups, et al.
/sbin Like /bin, but for programs used only by system managers (never by ordinary users).
and /usr/sbin
These work just like /bin and /sbin, except they are not needed when the system is in "restricted mode" for backups.
/usr/local The "local" area contains the stuff that was added on above and beyond the operating system itself. Within /usr/local will be /usr/local/bin and /usr/local/sbin directories for programs that were installed separately from the OS install.
(or /usr/home)
Each user will have their own subdirectory under /home. This subdirectory is for programs or data that are only used by a particular user.

Of course, on IBM i there is nothing in PASE that is required when the system is in restricted state. When in that state, you'd use traditional IBM i commands, you wouldn't use PASE commands. So in PASE or QShell, the IBM-supplied tools are typically in /usr/bin (for QShell) or /QOpenSys/usr/bin (for PASE).

For software that I add on, such as GnuPG (and other tools like 7-zip), I prefer to put it in /QOpenSys/usr/local so that it's kept separate from the operating system.

Setting Your PATH

Unix environments (including PASE and QShell) search for programs by looking in directories listed in a variable called PATH. For example, in PASE my PATH variable might look like this:


This is a list of IFS folder names separated by colons. If I type a command name (such as "gpg") it will first look in /QOpenSys/usr/bin for a program named gpg. If it's not found, it'll look in /QOpenSys/usr/sbin, and if it's still not found it'll look in . (which is Unix shorthand for "the current directory").

In order to make PASE understand that I also want to look in /QOpenSys/usr/local/bin, I might type the following command into PASE:

export PATH=$PATH:/QOpenSys/usr/local/bin

The $ character (dollar sign) tells PASE to insert the contents of a variable. So it will start by finding the current value of the PATH variable and insert it right before the :/QOpensys/usr/local/bin. That way, I'll set my PATH to whatever I had before, plus I'll add an additional directory to the search list.

This way, the extra programs that I add myself in the /QOpenSys/usr/local area will be in my PATH automatically, and I can just type the name of the command I want to run, I don't have to qualify it to tell it where the program is located.

Since I also use PASE for PHP and MySQL, I prefer to add those directories to my PATH as well. So I do this:

export PATH=$PATH:/QOpensys/usr/local/bin:/usr/local/Zend/Core/bin:/usr/local/mysql/mysql/bin

Typing that every time I start PASE is very cumbersome. Fortunately, you don't have to type it every time! Instead, you can put the command into a special file named .profile, and the command will automatically be run every time you start a PASE shell. For example, I can type the following within PASE.

echo "PATH=$PATH:/QOpensys/usr/local/bin:/usr/local/Zend/Core/bin:/usr/local/mysql/mysql/bin" >> $HOME/.profile

Linking /usr/local and /QOpenSys/usr/local

The addition of /QOpenSys for PASE can be problematic because some Unix tools (the ones not supplied by IBM) might expect to find their data or programs within the /usr/local folder, but in PASE they're normally stored in /QOpenSys/usr/local. So the utilities fail because they can't find their programs.

To solve this problem, after I install PASE, I always do the following from within PASE.

mkdir /usr/local
ln -s /usr/local /QOpenSys/usr/local

What this does is create a /usr/local directory in the root portion of the IFS. Then it creates a symbolic link from the /QOpenSys/usr/local to the root /usr/local. That way, any program that tries to access /QOpenSys/usr/local will actually be viewing /usr/local. It's like having two aliases for the same physical spot on disk. Now it doesn't matter if a program tries to access another program under /usr/local/bin or under /QOpenSys/usr/local/bin, since they point to the same place.

The above steps will work only if you don't already have a /QOpenSys/usr/local directory. If you do already have one, type the following instead (sorry, this can take a while to run):

mkdir /usr/local
cp -Rvp /QOpenSys/usr/local /usr/local
rm -rf /QOpenSys/usr/local
ln -s /usr/local /QOpenSys/usr/local

Download & Install

I had a hard time getting this tool to configure and compile on my system. It took me a lot of time and a lot of frustration, but in the end, I was victorious! I am now older and wiser than I was when I began. I hope to spare you the frustration that I experienced, so I will provide the tool already compiled. You can simply download, install, and run it on your system.

I've put download and installation instructions on my website at

The GPG Directory

In order to make it easy to see what GnuPG installs, and what you'd need to remove if you want to uninstall GnuPG, the installation process will install everything in the /QOpenSys/usr/local/gpg directory. The mklinks script will then create symbolic links between /QOpenSys/usr/local/gpg/bin and /QOpenSys/usr/local/bin so that the programs are in the correct places.

If you ever decide you want to uninstall GnuPG, all you'll have to do is the following:

   rm -rf /qopensys/usr/local/gpg
   rm /qopensys/usr/local/bin/gpg*

Need for a "Real" Unix Terminal

Now that you have GnuPG installed, you'll want to try it out. But before you can do that, you'll need a "real" Unix terminal.

Like SSH, GnuPG deals heavily with cryptography and security. And, like SSH, it's very fussy about the user's input. Anytime GnuPG attempts to ask you (the user) for any sort of input, it'll insist that you need to be on a "real" Unix terminal. If you try running something that requires input from a 5250 screen, it will give you the following message:

gpg: cannot open '/dev/tty': No such device or address

You can solve this problem by running the SSH Daemon (sshd) on IBM i and connecting to it via a Unix terminal emulator, such as PuTTY (which is a free Unix terminal for Windows).

Alternately, you can install VNC in PASE and connect to that. From VNC you can open an xterm and it'll treat that as a "real" Unix terminal.

Setting Up a User with Private Digital Keys

In order to use GnuPG to encrypt data, you'll need to generate digital keys for each user who runs the gpg tool. To do that, sign on as the user and type the following command to go to a PASE command prompt:

Since there are a lot of steps, I made a transcript of my session. I've used blue highlighting to indicate the parts that I typed. Anything without blue highlighting was printed on my screen. The yellow highlighting is not something I typed, but something I want to call your attention to after you've looked over my transcript.

$ gpg --gen-key
gpg (GnuPG) 1.4.10; Copyright (C) 2008 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: WARNING: using insecure memory!
gpg: please see for more information
gpg: directory '/home/TESTSCK/.gnupg' created
gpg: new configuration file `/home/TESTSCK/.gnupg/gpg.conf' created
gpg: WARNING: options in `/home/TESTSCK/.gnupg/gpg.conf' are not yet active during this run
gpg: keyring '/home/TESTSCK/.gnupg/secring.gpg' created
gpg: keyring '/home/TESTSCK/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection?<ENTER>
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)<ENTER>
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0)<ENTER>
Key does not expire at all
Is this correct? (y/N) Y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) "

Real name: Scott Klement
Email address:
Comment: Klement Sausage Co., Inc.
You selected this USER-ID:
    "Scott Klement (Klement Sausage Co., Inc.) "

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.
Enter passphrase:<ENTER>
Repeat passphrase:<ENTER>

You don't want a passphrase - this is probably a *bad* idea!
I will do it anyway.  You can change your passphrase at any time,
using this program with the option "--edit-key".

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/TESTSCK/.gnupg/trustdb.gpg: trustdb created
gpg: key FC74CCF5 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   2048R/FC74CCF5 2009-10-07
      Key fingerprint = 10E9 0F52 76EB 70A8 A4EF  0366 0A64 B096 FC74 CCF5
uid                  Scott Klement (Klement Sausage Co., Inc.) 
sub   2048R/65BFC5FA 2009-10-07


That may seem like a lot of steps, but it's actually pretty simple stuff. Once you've done it two or three times, you'll be able to fly through that process.

You'll notice the warning message that says, "WARNING: using insecure memory," above. I used yellow highlighting on that part to make it stand out. This indicates that the memory that the system is using to store the private digital key can potentially be written to the hard drive as part of the disk paging process. On Unix systems, there are ways to prevent that from happening, but on IBM i there are not. The single-level store nature of the system means that it'll always have a chance to page data to disk. It thinks this is insecure because someone who is able to get access to the physical disk could potentially find your digital keys. Even worse, on a Unix/Windows box, it's possible to save the paging file to a backup tape, where a stolen tape could compromise your keys. That part, at least, shouldn't be an issue on IBM i, but the gpg program doesn't know that.

At any rate, the warning message doesn't prevent gpg from working properly. However, if you get tired of seeing that warning, you can type the following command to disable it:

 echo "no-secmem-warning" > $HOME/.gnupg/gpg.conf

This command will have to be done separately for each user for whom you want to suppress the warning.

The digital key that you just generated is stored in the .gnupg directory that's in your user's home directory. This key is specific to that user and is private. Anyone who is able to get ahold of this key (especially if it's not passphrase protected) will be able to impersonate your user. Keep it secure, and you'll have a secure way of identifying your user.

Setting Up a User with Public Digital Keys

Now that your user has a private key, you may (depending on the circumstance) want to generate a public key. The public key would be sent to anyone who needs to send files to you. When they encrypt the file, they will encrypt it according to your public key. Files that are so encrypted can be decrypted only by someone with your private key—and that's why you need to keep that private key secure. (Did I mention that you need to keep your private key secure?)

To generate a public key, type the following:

gpg --armor --output my_pubkey.txt --export 'Scott Klement'

Obviously, the part where I put 'Scott Klement' must be changed to a more appropriate value for your user. This value has to match what you specified for 'Real Name' when you generated the private key. It will use this value to locate the private key, and use data in the private key to generate the public key.

The output will be placed in my_pubkey.txt, and this will be a plain-text file that you can attach to an email, or FTP in ASCII mode, or even CPYFRMSTMF to put it into a physical file if you desire. In the end, the goal is to get this public key file to whomever needs to encrypt files and send them to you.

On the other hand, if you will be the one encrypting files, the sender (whomever is encrypting the files) will need to generate a private key. For the sake of example, let's say that I will be creating files, encrypting them, and sending them to Mr. Obama. (Why not?) Before I can do that, I need Obama's public key. So he'll go to his computer and type the following:

gpg --armor --output presidential_key.txt --export 'Barack Obama'

He will then send me the presidential_key.txt file somehow. Maybe it'll be email. Maybe it'll be FTP. However it works, I get the file and put it into the /home/klemscot directory on my IBM i.

Installing Someone Else's Public Key

Since I want to encrypt files to send to someone else, I have to install that person's public key into my personal "keyring." That will make that key available for me to use when encrypting a document.

gpg --import presidential_key.txt

The preceding code took Mr. Obama's public key and imported it into my keyring so I can use it when I want to encrypt a file. However, at this point, the system doesn't know if it can trust Obama. (Not because they don't like his politics! Because nobody has signed his digital key.)

What I need to do next is make sure I really got this key from Obama (or whomever I got it from) and verify that they really sent it to me, and so on and so forth. A simple phone call might do the trick. I'll call him up and make sure he really sent me a key, just to make sure this is legitimate, and not some hacker sending me a key in hopes of being able to decrypt my next file transfer.

Once I've verified that the key is really from Obama, I will tell GnuPG to "sign it." That will take data from my private key and data from Obama's public key and create a digital signature.

gpg --sign-key 'Barack Obama'

It will show you some "fingerprint" numbers that identify the key. You can use this in your phone call to make sure you have the correct key if you like. It will ask, "Are you sure you want to sign this key with your key?" Answer Y for yes.

You'll now have a signed key in your keyring, ready to use in encryption.

Encrypting a File

Before encrypting a file, make sure it's in the final file format—that is, the file format you want it to be in when it's unencrypted. You won't be able to successfully convert the file from EBCDIC to ASCII (for example) after it's been encrypted. So start out by doing a CPYTOSTMF or CPYTOIMPF to copy the file to the IFS, and make sure you choose the options to translate the file to ASCII (or whatever you intend it to be received as) and put it in the correct file format.

When you're ready, you can encrypt it from PASE with the following command:

gpg --encrypt --recipient 'Barack Obama' --output encrypted.txt.gpg inputfile.txt

The .GPG extension is just a convention that helps identify the type of file. You can specify any filename that you like for the input and output file formats. This command will encrypt the file according to the signed digital key for the recipient (in this case, it looks up 'Barack Obama' in my keyring to find the appropriate keys).

When it's done, I'll have a file named encrypted.txt.gpg that's appropriately encrypted.

I can now send this file to the recipient. It's very important that you send the file in a binary-safe manner. Don't allow a file transfer program to attempt to translate the file from EBCDIC to ASCII (or vice-versa), because this file is no longer made up of text characters. Copying the file from a mapped drive will probably work fine (unless you have specifically configured it to translate .gpg files!). FTP will also work, provided that you use binary mode. The OpenSSH tools like SCP and SFTP will always work in binary mode, so they will also work fine for this type of transfer.

Decrypting the File

When Obama receives my file, he'll need to decrypt it before he'll be able to use it. He'll need to know the passphrase (if any) that he put on his own private key in order to decrypt it. Since I encrypted it using is public key, only his private key can decrypt it.

gpg --output outputfile.txt --decrypt encrypted.txt.gpg

If he has a passphrase on his private key, this will ask him for the passphrase, and then it'll decrypt it and put the result into the outputfile.txt file. IF all is well, the outputfile.txt file should be byte-for-byte identical to the inputfile.txt that I built with CPYTOIMPF.

Note that Obama needs to know only his own passphrase; he never needs to know mine. That's one of the secure features of GnuPG—you never have to give your password out to anyone else. When you gave them your public key, you made it possible for them to generate a file that you can decrypt with your private key. No need to have their password!

Automating the Process

Any time GnuPG needs to ask the user a question related to a passphrase, it will always require a "real" Unix terminal. (Actually, you might be able to fake it out using an Expect script, but I haven't had a chance to try that yet. That's an experiment for another day!)

In situations in which no passphrase was used, however, it's possible to automate the process by using a CL program or a shell script.

If GnuPG has to ask you a question, the CL program or shell script will fail. So it's important to suppress GnuPG from asking any questions to the user if you are going to automate the process. For example, if it asks "Output file exists. Should I replace it?" your CL program would fail. To combat that problem, GnuPG provides three different options:

For example, you could code the following CL program to encrypt a file:



   CHGVAR VAR(&CMD) VALUE('gpg --batch --yes +
                               --encrypt +
                               --recipient "Barack Obama" +
                               --output "encrypted.gpg" +

   CALL QP2SHELL PARM('/QOpenSys/usr/bin/sh' '-c' &CMD)

   /* FIXME: Add code to verify that it succeeded!! */


Of course, the CL program currently has no way to detect whether the gpg command succeeded or failed, and any output printed on the screen is discarded. That could be troublesome! If gpg fails, it will set its exit status to a non-zero value, just as SSH does. Therefore, it might be better (and certainly easier) to use QShell's command interface instead of the QP2SHELL API to invoke gpg. QShell has an environment variable that lets it report errors via an *ESCAPE message, just like a CL command would:




   CHGVAR VAR(&CMD) VALUE('PATH=$PATH:/QOpenSys/usr/bin && +
                           gpg --batch --yes +
                               --encrypt +
                               --recipient "Barack Obama" +
                               --output "encrypted.gpg" +



This program will end in error if something is wrong, whereas the QP2SHELL version would've just continued without noticing the error.

You might also like to use my UNIXCMD utility for running QShell/PASE tools from RPG. If any error messages are printed to the screen, my tool will return them to the program (via an RPG READ opcode.) If the command fails, the CLOSE opcode will report an error.

In this example, messages from GnuPG are saved into a variable, and if the CLOSE opcode returns an error, it will use the QUILNGTX API to display the error information in a window on the 5250 display:

     FUNIX      CF   F 1000        SPECIAL PGMNAME('UNIXCMD')
     F                                     PLIST(UNIXPARM) USROPN

     D ErrorEscape     ds                  qualified
     D   bytesProv                   10i 0 inz(0)
     D   bytesAvail                  10i 0 inz(0)

     D QUILNGTX        PR                  ExtPgm('QUILNGTX')
     D   text                     65535a   const options(*varsize)
     D   length                      10i 0 const
     D   msgid                        7a   const
     D   qualmsgf                    20a   const
     D   errorCode                32767a   options(*varsize)

     D cmd             s           5000a
     D mode            s              1A   inz('P')
     D record          ds          1000
     D error           s          65535a   varying

     C     UNIXPARM      PLIST
     C                   PARM                    CMD
     C                   PARM                    MODE
          cmd = 'gpg --batch --yes +
                     --encrypt +
                     --recipient "Barack Obama" +
                     --output "encrypted.txt" +
          open UNIX;

          read UNIX record;
          dow not %eof(UNIX);
             error += ' ' + %trimr(record);
             read UNIX record;

            close UNIX;
            QUILNGTX(error:%len(error):' ':' ':errorEscape);

          *inlr = *on;

That makes error handling about as easy as I can make it. For example, if the program fails because the system can't find inputfile.txt, this program would print the following on the screen:

                                 Command Entry                              
                                                           Request level:   1   
 All previous commands and messages:                                            
    > call unixex10                                                             
 :                                                                            : 
 :   gpg: can't open `inputfile.txt': No such file or directory gpg:          : 
 :   inputfile.txt: encryption failed: file open error                        : 
 :                                                                            : 
 :                                                                            : 
 :                                                                            : 
 :                                                                            : 
 :                                                                    Bottom  : 
 :  F12=Cancel                                                                : 
 :                                                                            : 
 Type command, press Enter.                                                     
 ===> call unixex10                                                             
 F3=Exit   F4=Prompt   F9=Retrieve   F10=Exclude detailed messages              
 F11=Display full      F12=Cancel    F13=Information Assistant   F24=More keys  

More Information

Well, that's about as much information about GnuPG as I've been able to gather in the past two days. I'm having fun. Anyway, here are some links that you'll find helpful. I've collected as much information about GnuPG as I've been able to and put links on my website. This is also where you go to download the GnuPG software:

The following titles were previously published articles on System iNetwork. Since this site is now defunct, the following links will no longer work. I'm keeping the titles here in case you happen to have a System iNetwork archive on CD from the old days, then you can look them up on the CD.