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.
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).|
|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.|
|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.
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:
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:
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
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
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 scottklement.com/gnupg.
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*
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.
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 http://www.gnupg.org/faq.html 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: firstname.lastname@example.org 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.
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.
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.
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.
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!
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:
--batch: Run in "batch" (non-interactive) mode. This prevents it from asking questions that don't really pertain to security. For example, you can't use this to bypass a passphrase, but you can use it to bypass a "replace this file?" question.
--yes: If --batch is given, any question (like "replace?") would be automatically answered with a yes.
--no: If --batch is given, any question (like "replace?") would be automatically answered with a no.
For example, you could code the following CL program to encrypt a file:
PGM DCL VAR(&CMD) TYPE(*CHAR) LEN(500) CHGVAR VAR(&CMD) VALUE('gpg --batch --yes + --encrypt + --recipient "Barack Obama" + --output "encrypted.gpg" + inputfile.txt') CALL QP2SHELL PARM('/QOpenSys/usr/bin/sh' '-c' &CMD) /* FIXME: Add code to verify that it succeeded!! */ ENDPGM
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:
PGM DCL VAR(&CMD) TYPE(*CHAR) LEN(1000) ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y) + REPLACE(*YES) CHGVAR VAR(&CMD) VALUE('PATH=$PATH:/QOpenSys/usr/bin && + gpg --batch --yes + --encrypt + --recipient "Barack Obama" + --output "encrypted.gpg" + inputfile.txt') QSH CMD(&CMD) ENDPGM
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 /free cmd = 'gpg --batch --yes + --encrypt + --recipient "Barack Obama" + --output "encrypted.txt" + inputfile.txt'; open UNIX; read UNIX record; dow not %eof(UNIX); error += ' ' + %trimr(record); read UNIX record; enddo; monitor; close UNIX; on-error; QUILNGTX(error:%len(error):' ':' ':errorEscape); endmon; *inlr = *on; /end-free
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
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.