Using ICSF in a multi system environment

Using ICSF within a single image or sysplex discussed setting up ICSF in a single environment, where systems can share the ICSF datasets.

This blog post is when you have multiple systems which do not share the ICSF data sets.

You have some encrypted data sets which you have sent to your backup system. How do you get the ICSF keys to your backup site? I will focus on using AES CIPHER keys because AES is recommended over DES keys; and CIPHER keys are recommended over DATA keys.

The challenge of using ICSF in a multi system environment where the ICSF data sets are not shared, is setting up the first keys. This is not easy as ICSF does not provide all the functionality to do this.

If your systems have the same hardware key, you can unload and reload a key using my programs in EASYICSF on Github. You unload the key (encrypted with the hardware key) to a data set, send the data set to the remote system, and reload the key.

If your systems do not use the same hardware key, you can fairly easily set up the keys for use between systems if you can accept some clear text key value sharing.

If your systems do not use the same hardware key, the most secure way of setting up keys is where the key value is never in clear text, and so cannot be copied or used in an authorised way. This has many steps, and I’ll cover this in another blog post (Generating ICSF keys using Diffie-Hellman).

If you want to encrypt a key to send to a remote site, ICSF provides Key Encryption Key (KEK) technology. Once they KEK has been set up you can export a key from the local system, encrypting it with an exporter KEK. Send the encrypted data to the remote site, and use the matching KEK to import it. At no point is the clear text version of the key exposed. I think of a KEK as an asymmetric key for data. Having the exporter KEK does not mean you can decrypt the data.

You need to have an exporter key to get from system A to system B, and an exporter key to get from system B to system A. If you specify the key value yourself these could be the same. If you let the system generate the key value, they will be different.

Once you have keys set up, you can use ICSF Key Encryption Key (KEK) to send keys securely to another system. The key phrase is “Once you have set up…”.

How long are keys needed for ?

A cipher key used in data set encryption is needed for as long as the data set using it is needed, so potentially many years. Deleting the key means the data set cannot be read.

When you create a KEK, you create both the exporter and importer at the same time. You cannot generate one from the other. A KEK is only used when transferring keys between systems. You could have a new KEK every week, with no impact. You just need to ensure you have at least another KEK to encrypt it.

How many KEK’s do I need?

If you have three systems, Production, Backup and Disaster Recovery (DR). You could define an exporter key on production, and the same importer key on Backup and DR. This might be easy to implement, but I think I would feel more comfortable with different KEKs:

  • Exporter on Production and Importer on Backup,
  • Exporter on Production and Importer on DR,
  • Backup to DR

and the opposite; Exporter on Backup and Importer on Production etc.

I included the system names in the data set used to store the key. When I initially used the same name for all definitions, such as ‘COLIN.EXPORTED.KEK’, I found I was getting into a muddle, and had the wrong key in it.

To export and import AES CIPHER keys see export a key from CKDS into a data set so it can be imported on another system and import a key from a data set into the CKDS on Github. You could just use unload and reload a key using my programs in EASYICSF on Github!

Overview to creating keys in a multi system environment

  • If the hardware keys are the same on each system, you can use UNLOAD and RELOAD programs in my EASYICSF on Github which allows you to copy the hardware encrypted key from one ICSF data set to an intermediate data set, then reload it into an ICSF data set. See here.
  • You can generate data set encryption keys on each system using a cut and paste of the JCL. This is not very secure because the key value is visible in clear text. See here.
  • Define Key Encrypting Keys for sending keys encrypted between systems. See here for detailed instructions.
    • On the sender system define a temporary exporter key specifying the key value in clear text.
    • On the receiving system(s) define the matching importer key using the same key values (cut and paste) and a small edit.
    • Define a permanent exporter key letting the system securely generate the key value, and specify the temporary KEK to encrypt the importer key.
    • Send the encrypted permanent importer KEK to the remote system.
    • On the receiver systems use the temporary importer KEK to import the permanent key
    • Delete the temporary KEK from each system.
    • You now have KEKs on your system, and the window where there was clear text visible was small.
  • Locally generate a secure data set encryption key, and export it to the remote systems as needed. See here.
    • You generate a data set encryption key.
    • For each system you want to send this key to you export it, using the exporter KEK for that system.
    • If you want to send the key at a later date, you just export the key using the appropriate KEK.
  • Generating secure data set encryption keys, both local and encrypted with a KEK
    • ICSF has a utility CSFKGUP which, in one request, can generate a key for use locally, and export it using an exporter KEK.
    • This does not feel a very good solution to me.
      • If you want to send the key to multiple systems, you need to use the same KEK for all systems.
      • If, at a later date, you want to send the key to another system you have to export it (see above). It is easier just to export it anyway.

These may look relatively simple – but ICSF does not provide a utility to export and import the keys. You can write your own C or REXX program, or use the ones I have written. See export a key and import a key in EasyICSF on Gitub.

Detailed instructions

Use unload and reload.

This is based on unload and reload a key using my programs in EASYICSF on Github.

Unload

//IBMUNL  JOB 'COLIN',CLASS=A,REGION=0M,COND=(4,LE) RESTART=RUN 
//JOBLIB JCLLIB ORDER=(COLIN.ICSF.C,CBC.SCCNPRC)
//* COMPILE EXEC CCPROC,PROG=UNLOAD
//*
//*
// SET KEY='-key SECKEY2 '
// SET DB='-debug 8 '
// SET DB=' '
//RUN EXEC PGM=UNLOAD,REGION=0M,
// PARM='&KEY. &DB '
//STEPLIB DD DISP=SHR,DSN=COLIN.ICSFLOAD
//SYSPRINT DD SYSOUT=*,DCB=(LRECL=200)
//SYSOUT DD SYSOUT=*
//KEY DD DSN=COLIN.UNLOAD.AES,DISP=(OLD)
//* SPACE=(CYL,(1,1)),DCB=(LRECL=7000,RECFM=V)

Reload

//IBMRELOA JOB 'COLIN',CLASS=A,REGION=0M,COND=(4,LE) RESTART=RUN 
//JOBLIB JCLLIB ORDER=(COLIN.ICSF.C,CBC.SCCNPRC)
//*OMPILE EXEC CCPROC,PROG=RELOAD
//*
//*
// SET DB='-debug 8 '
// SET KEY='-key SECKEY2 '
//RUN EXEC PGM=RELOAD,REGION=0M,
// PARM='&KEY. &DB '
//STEPLIB DD DISP=SHR,DSN=COLIN.ICSFLOAD
//SYSPRINT DD SYSOUT=*,DCB=(LRECL=200)
//SYSOUT DD SYSOUT=*
//KEY DD DSN=COLIN.UNLOAD.AES,DISP=(OLD)

An easy, not very secure method, for use in a proof of concept.

You can use JCL Like

//IBMICSF JOB 1,MSGCLASS=H
//STEP10 EXEC PGM=CSFKGUP
// SET CKDS=COLIN.SCSFCKDS
//CSFCKDS DD DISP=OLD,DSN=&CKDS
//CSFIN DD *,LRECL=80
ADD TYPE(CIPHER) ALGORITHM(AES),
KEYMGT(XPRTCPAC),
CLEAR,
KEY(0123456789ABCDEF,0123456789ABCDEF,
0123456789ABCDEF,0123456789ABCDEF),
LABEL(COLINCIPHERCLEAR ) ,
KEYUSAGE(ANY-MODE)
/*
//CSFDIAG DD SYSOUT=,LRECL=133 //CSFKEYS DD SYSOUT=,LRECL=1044
//CSFSTMNT DD SYSOUT=,LRECL=80 // REFRESH THE IN MEMORY DATA
//REFRESH EXEC PGM=CSFEUTIL,PARM='&CKDS,REFRESH'

You run this on every system where you need the data set encryption key.

You need to specify for data se

  • KEYMGT(XPRTCPAC),
  • KEYUSAGE(ANY-MODE)
  • KEY(….) You need to pick a set of random hexadecimal numbers.

You can run this job on all systems you need the data set encryption keys on, with no changes.

This is not secure because other people may be able to see the keys…

  • You may have saved the JCL containing the key value (for future use) either in a data set or on paper
  • If you ran this as a job, the values may be in the spool output. Depending on how you specified the output class.
  • Copying the values to the remote system may get intercepted.

Defining KEKs

If you want to encrypt a key to send to a remote site ICSF provides Key Encryption Key (KEK) technology. Once the KEK has been set up you can export a key from the local system, encrypting it with an exporter KEK. Send the encrypted data to the remote site, and use the matching KEK to import it. At no point is the clear text version of the key exposed.

Generate a bootstrap KEK.

This bootstrap KEK is only used to set up a permanent KEK.

In a similar way to defining the data set encryption key above you can use

//IBMICSF  JOB 1,MSGCLASS=H 
//STEP10 EXEC PGM=CSFKGUP
// SET CKDS=COLIN.SCSFCKDS
//CSFCKDS DD DISP=OLD,DSN=&CKDS
//CSFIN DD *,LRECL=80
DELETE TYPE(EXPORTER) LABEL(TEMPATOB)
ADD LABEL(TEMPATOB)TYPE(EXPORTER) CLEAR ,
ALGORITHM(AES),
KEY(3123456789ABCDEF,0123456789ABCDEF)
/*
//CSFDIAG DD SYSOUT=*,LRECL=133
//* REFRESH THE IN MEMORY DATA
//REFRESH EXEC PGM=CSFEUTIL,PARM='&CKDS,REFRESH'

On the remote system, use the same JCL except with TYPE(IMPORTER). You will, of course, use different hex values in the key to those I gave.

Generate the long term KEK.

//IBMICSF  JOB 1,MSGCLASS=H 
//STEP10 EXEC PGM=CSFKGUP
// SET CKDS=COLIN.SCSFCKDS
//CSFCKDS DD DISP=OLD,DSN=&CKDS
//* LENGTH(32) GENERATES A 256 BIT KEY
//CSFIN DD *,LRECL=80
ADD LABEL(ATOBPERM) TYPE(EXPORTER) ,
TRANSKEY(TEMPATOB) ,
ALGORITHM(AES) LENGTH(32)
/*
//CSFDIAG DD SYSOUT=*,LRECL=133
//CSFKEYS DD DSN=COLIN.CSFKEYS,DISP=OLD
//CSFSTMNT DD SYSOUT=*,LRECL=80
//* REFRESH THE IN MEMORY DATA
//REFRESH EXEC PGM=CSFEUTIL,PARM='&CKDS,REFRESH'

This creates a key value, stores the exporter KEK in the local CKDS encrypted with the hardware keys. It encrypts the new importer KEK with the TEMPATOB KEK and stores it in the CSFKEYS dataset.

The layout of this CSFKEYS data set is:

  • ATOBPERM the name of the object,
  • IMPORTER you need this sort of key to import it,
  • ATOB this is the KEK name used to encrypt it
  • The key for ATOBPERM with the key value encrypted. This is a hexadecimal value.

Knowing the information in the CSFKEYS data set you can import the key into the CKDS on the remote system, using the TEMPATOB importer key.

ICSF does not provide a utility for this. You can write some REXX code, or you can use my programs. See import a key from a data set into the CKDS.

Delete the temporary KEK

You can use the JCL

//IBMICSF  JOB 1,MSGCLASS=H 
//STEP10 EXEC PGM=CSFKGUP
// SET CKDS=COLIN.SCSFCKDS
//CSFCKDS DD DISP=OLD,DSN=&CKDS
//CSFIN DD *,LRECL=80
DELETE TYPE(EXPORTER) LABEL(TEMPATOB)
/*
//CSFDIAG DD SYSOUT=*,LRECL=133
//CSFKEYS DD DUMMY
//CSFSTMNT DD DUMMY
//* REFRESH THE IN MEMORY DATA
//REFRESH EXEC PGM=CSFEUTIL,PARM='&CKDS,REFRESH'

Locally generate a secure data set encryption key

If you do not have a data set encryption key, use JCL like

Securely generate data set encryption keys.

//IBMDSENC JOB 1,MSGCLASS=H 
//STEP0 EXEC PGM=CSFKGUP
//*
// SET CKDS=COLIN.SCSFCKDS
//CSFCKDS DD DISP=OLD,DSN=&CKDS
//* LENGTH(32) GENERATES A 256 BIT KEY
//CSFIN DD *,LRECL=80
ADD TYPE(CIPHER) ALGORITHM(AES) LENGTH(32),
LABEL(DSENCRYPTION),
KEYMGT(XPRTCPAC),
KEYUSAGE(ANY-MODE)
/*
//CSFDIAG DD SYSOUT=*,LRECL=133
//CSFKEYS DD DUMMY
//CSFSTMNT DD DUMMY
//* REFRESH THE IN MEMORY DATA
//REFRESH EXEC PGM=CSFEUTIL,PARM='&CKDS,REFRESH'
//

You can then send the CSFKEYS data set to the remote site, and import it using the ATOBPERM importer key. See import a key from a data set into the CKDS on Github.

Using keys across systems

You can only use keys which are within their validity dates: crypto-period start date and
cypto-period end date.

When you import a key, meta data from the original site is not included, so you might need to specify start date, stop date, archive flags etc. See set meta data attributes on Github.

Generating ICSF keys using Diffie-Hellman

The Diffie-Hellman technique allows you to create keys on two system securely, with no need to share a common key. Each system needs a private/public key pair, and the public keys are available on all systems. There are many steps but it is relatively painless.

Scenario

Two systems A and B. We want to have a data set encryption key on system A copied to system B and to create an exporter key on A, and a matching importer key on B.

The steps are

  • generate private/public pairs on each system
  • use these to generate the symmetric keys.

Generate a private/public key pair on each system, and send the public key to the other system.

On System A

Use the ISPF ICSF panels

  • Options 5;6;6 Generate PKA keys, import or export public keys via certificate
    • Enter PKDS record’s label: SYSTEMA
    • Select Generate a new RSA or EC key pair record
    • Select EC NIST Curve, P521.
    • This should display PKDS Key Request Successful. Press enter to return.
    • Select Export the PKDS record’s public key to a certificate data set
    • Specify a data set name ‘COLIN.SYSTEMA’
    • Specify the common name SYSTEMA
    • Press enter
    • This should give you PKDS Public Key Export Successful
  • Send the data set ‘COLIN.SYSTEMA’ to the remote system.

On System B

  • Follow the instructions above to generate PKA keys.
    • Label Name SYSTEMB, and use export data set name COLIN.SYSTEMB, etc. Check it has worked.
    • Next, select Create a PKDS public key record from an input certificate
    • Enter the name of the data set you copied across containing the public key for SYSTEMA
    • Press enter. It should import the key
    • F3 to return to the ICSF PDKS keys page.
    • Select option 1, and press enter. Use the line command K to display the contents of the certificates. You should have the private key with sections “PRIVATE PUBLIC”, and the public key should have section “PUBLIC”
  • Send the data set ‘COLIN.SYSTEMB’ to the first system.

On System A

  • Read in the data set
  • Options 5;6;6 Generate PKA keys, import or export public keys via certificate
  • Enter PKDS record label SYSTEMB
  • Select Create a PKDS public key record from an input certificate
  • Enter the name of the data set COLIN.SYSTEMB.
  • When this has worked, display the contents of the PKDS as described above.

You now have a private/public key on each system, and each system has a copy of the other’s public key.

Generate a symmetric key on each system

Now you have the private/public key on each system, and the public key of the other system, you can create a key.

ICSF does not provide a function for this. See Generate a secure shared key on multiple systems using Diffie-Hellman in Github.

  • On SYSTEMA you need the name of the private/public pair SYSTEMA, and the name of the public certificate SYSTEMB.
  • You need a phrase which provides a seed to the encryption. This does not need to be kept confidential, but you may still wish to protect it.

Generate a CIPHER for data set encryption

//RUN      EXEC PGM=GENDH,REGION=0M,PARMDD=MYPARMS 
//MYPARMS DD *
-ptype INTERNAL,AES,CIPHER,XPRTCPAC,ANY-MODE
-key AESDHDSCIPHER
-private AAA
-public BBB
-replace Y
-party cOlinSeed
-debug 0
/*
//STEPLIB DD DISP=SHR,DSN=COLIN.LOAD
//SYSPRINT DD SYSOUT=*,DCB=(LRECL=200)
//CEEDUMP DD SYSOUT=*,DCB=(LRECL=200)
//SYSOUT DD SYSOUT=*
//SYSERR DD SYSOUT=*

On the other system change AAA and BBB, and run the JCL.

Create an exporter key on SYSTEMA

//RUN      EXEC PGM=GENDH,REGION=0M,PARMDD=MYPARMS 
//MYPARMS DD *
-ptype INTERNAL,AES,EXPORTER
-key AESDHKEK
-private AAA
-public BBB
-replace Y
-party cOlinSeed2
-debug 0
/*
//STEPLIB DD DISP=SHR,DSN=COLIN.LOAD
//SYSPRINT DD SYSOUT=*,DCB=(LRECL=200)
//CEEDUMP DD SYSOUT=*,DCB=(LRECL=200)
//SYSOUT DD SYSOUT=*
//SYSERR DD SYSOUT=*

Create a matching importer key on SYSTEMB

//RUN      EXEC PGM=GENDH,REGION=0M,PARMDD=MYPARMS 
//MYPARMS DD *
-ptype INTERNAL,AES,IMPORTER
-key AESDHKEK
-private BBB
-public AAA
-replace Y
-party cOlinSeed2
-debug 0
/*
//STEPLIB DD DISP=SHR,DSN=COLIN.LOAD
//SYSPRINT DD SYSOUT=*,DCB=(LRECL=200)
//CEEDUMP DD SYSOUT=*,DCB=(LRECL=200)
//SYSOUT DD SYSOUT=*
//SYSERR DD SYSOUT=*

You can now use exporter key AESDHKEK on system A, and importer KEK AESDHKEK on system B.

Getting a trace from ICSF using CTRACE

I had a problem with a certificate and as part of trying to track down the problem, I traced all of the components. I found the documentation for tracing ICSF was not complete – it assumed you were going to take a dump.

You can collect a CTRACE from ICSF, and have it go to a CTRACE dataset.

Create the CTICSFxx member in parmlib

I copied CTICSF00, added the writer statement, and said collect ALL

TRACEOPTS 
          ON 
          BUFSIZE(2M) 
          wtr(ctwtr) 
          OPTIONS('ALL') 

Turn off the ICSF trace

TRACE CT,OFF,COMP=CSF   

Start the trace writing started task

See Create a procedure to collect the trace. The same trace writer can be used for all ctrace record.

TRACE CT,WTRSTART=CTWTR  

Start CTRACE

You need a parmlib member to contain the definitions I called mine CTICSF01

TRACEOPTS 
/*-----------------------------------------------------------------*/
/* ON OR OFF: PICK 1 */
/*-----------------------------------------------------------------*/
ON
/* OFF */
BUFSIZE(2M)
wtr(ctwtr)
/*-----------------------------------------------------------------*/
/* OPTIONS: NAMES OF FUNCTIONS TO BE TRACED, OR "ALL", OR "MIN" */
/*-----------------------------------------------------------------*/
/* OPTIONS( */
/* 'ALL' */
/* ,'KDSIO' */
/* ,'CARDIO' */
/* ,'SYSCALL' */
/* ,'DEBUG' */
/* ,'RDIO' */
/* ,'RDDATA' */
/* ,'MIN' */
/* ) */
OPTIONS('KDSIO','CARDIO','SYSCALL','RDIO','ALL')

Start the trace

TRACE CT,ON,COMP=CSF,PARM=CTICSF01

This gave me

TRACE CT,ON,COMP=CSF,PARM=CTICSF01
IEE252I MEMBER CTICSF01 FOUND IN USER.Z24C.PARMLIB
ITT038I ALL OF THE TRANSACTIONS REQUESTED VIA THE TRACE CT COMMAND WERE SUCCESSFULLY EXECUTED.

Run your failing transaction

Stop ctrace

TRACE CT,OFF,COMP=CSF 

This gave

ITT038I ALL OF THE TRANSACTIONS REQUESTED VIA THE TRACE CT COMMAND WERE SUCCESSFULLY EXECUTED.

Stop the trace writer

TRACE CT,WTRSTOP=CTWTR

This gave messages

ITT111I CTRACE WRITER CTWTR TERMINATED BECAUSE OF A WTRSTOP REQUEST.
IEF404I CTWTR – ENDED – TIME=07.54.33
IEF196I IEF142I CTWTR CTWTR – STEP WAS EXECUTED – COND CODE 0000
IEF196I IGD104I IBMUSER.CTRACE1 RETAINED

Any data is in the dataset.

Go into IPCS.

The instructions below show how to find statements with a non zero return code.

  • =0 – and enter the data set name
  • =6 – to get to the ipcs command screen
  • dropd – to tell IPCS to forget any information it may know about for the dataset
  • CTRACE COMP(CSF) full – this displays any ICSF CTRACE data
  • m PF8 – go to the bottom of the data
  • report view – to go into ISPF View mode on the data set
  • X ALL – to hide all of the data
  • f code all – this shows any error codes
  • c ‘Return code = 00000000 ‘Return ZZZZ = 00000000’ all – use cut and paste of the string, as blanks are lost in this post
  • c ‘Condition code = 00000000’ ‘Condition ZZZZ = 00000000’ all
  • x all
  • f code all – this will show you are statements with a non zero return code.

Certificate dates with ICSF and PKISERV

Digging into certificates provided by ICSF, I got confused with the dates.

There are 3 places that start/end dates can be, and they have different meanings and uses.

  1. The validity period you see in the certificate is part of the certificate itself. That is added at certificate creation and enforced by the application (such as System SSL).
  2. Within ICSF there are fields START DATE/END DATE you see in the panels are CKA_START_DATE and CKA_END_DATE. They are defined in the PKCS#11 standards but are not enforced.
  3. Within record metadata for a KDSR format record, you will see Cryptoperiod start date/Cryptoperiod end date. This is enforced by ICSF. Usage outside this time frame is not permitted.

Note that PKCS#11 services are the only place you can see all three of these. Neither CKDS nor PKDS can hold certificates, nor do they support PKCS#11 attributes.

Official document

The standards document PKCS #11 Cryptographic Token Interface Base Specification says

CKA_START_DATE – Start date for the certificate (default empty)
CKA_END_DATE – End date for the certificate (default empty

Section 4.6.2 (Certificate objects Overview):
The CKA_START_DATE and CKA_END_DATE attributes are for reference only; Cryptoki does not attach any special meaning to them. When present, the application is responsible to set them to values that match the certificate’s encoded “not before” and “not after” fields (if any).

Section 4.7.2 (Key Objects Overview) has similar wording:
Note that the CKA_START_DATE and CKA_END_DATE attributes are for reference only; Cryptoki does not attach any special meaning to them. In particular, it does not restrict usage of a key

Thanks to Eric Rossman, for helping me understand this.

Getting cipher keys to another site – the basics of Exporter and Importer keys in ICSF.

I’ve spent some time (weeks) exploring ICSF with the overall mission of sending an encrypted data set between two sites. Looking back it was like the saying when you’re up to your neck in alligators, it’s hard to remember that your initial objective was to drain the swamp.

I’ve explored many parts of ICSF. One area that confused me for a while was the use of Key Encrypting Keys, or Exporter and Importer keys, also known as transport keys. I’ll explain my current thoughts on it – bearing in mind these may not be 100% accurate.

If I want to encrypt data on one system, send the encrypted data to another system, and decrypt it on that system; the sender system needs encryption key, and the receiving system needs the decryption key.

Typically the data encryption is done using a symmetric key, where the same key is used to encrypt as decrypt. You can also use asymmetric keys where you encrypt with one key, but need a different key to decrypt it.

The first challenge is how to securely get the keys onto the systems.

  • You cannot just email the symmetric key to the remote site, because bad people monitoring your email will be able to get the symmetric key.
  • You could print the key, and sent it through the mail system, courier, or carrier pigeon to the remote site. This still means that bad guys could get the key (using a telephoto lens through a window, through the security cameras, or catch the pigeon).
  • A secure way using a technique called Diffi-Hellman can be used to create the same symmetric key at each end. It uses private/public keys and an agreed seed. No sensitive data is sent between the two systems.

When setting up ICSF to use cross system, you need to set up keys for both A to B, and for B to A. You can use the same keys and seed for both, but the keys will be different.

If you are setting up several independent systems you will need keys A to B, B to A, A to C, C to A, B to C and C to B etc.

Setting up keys

You can set up a CIPHER key for encrypting data, and you can set up a MAC key for checking that what was sent is the same as what was received. (You hash the data, then encrypt the hashed value. Send the hash along with the data. If both ends do it, they should get the same answer!)

Decrypting and re-encypting

The keys are stored on disk in an encrypted format. The key for this is within the cryptographic hardware. If you want to send a key to another system, you need to encrypt it.

ICSF has a function in the hardware which says “here is some data encrypted with the hardware key, decrypt it, and re-encrypt it with this other key”. It has a matching function “here is some encrypted data, decrypt it, and re-encypt it with the hardware key”. This way the clear text of the key is never seen. To extract a key, you need to provide a key to re-encrypt it.

How to send the key to the remote system?

You have several choices depending on the level of security your enterprise has.

ICSF has a function to export a symmetric key using

  • an RSA public key. At the remote end you need the private key to be able to decrypt it. This needs an RSA key size of at least 2048.
  • an exporter key. At the remote end you need the matching importer key to decrypt it. Under the covers this is a symmetric (AES) key. The AES technique is faster, and “stronger” than RSA (it takes longer to break it). An AES 256 key is considered stronger than an RSA 4096 key.

I think of exporter and importer keys as a generalised public key and private key. The concept may be the same, but the implementation is very different.

The question “should I use RSA or Exporter/Importer” comes down to how secure do you want to be. If you export a key once a week the costs are small.

These keys used to encrypt keys, are known as key encrypting keys, and you often see KEK in the documentation.

What are the advantages of using an exporter and an importer key?

If you are using a symmetric key to encrypt your data, why is there exporter and importer keys, and not just one key?

If a purely symmetric key was used as a key encryption key, this means that if you are allowed to encrypt with it – you can decrypt with it. This then means you can decrypt other people’s data which used the same key.

By having a one key for encryption and another key for decryption you can isolate the authorities. If I have access to the exporter key, although I extract it and send it to my evil colleague at the remote end, they cannot use it to decrypt the data. If I have access to a symmetric key generated with Diffi-Hellman, the same key is used at each end, and so I could extract it and send it to my evil colleague who could use it.

Setting up my first importer or exporter key.

Use a pair of matching ECC public and private keys. The key type (eg Brain Pool) and key size must match. You use CSNBKTB2 to define a skeleton of type importer or exporter (or cipher). You then pass the skeleton into CSNDEDH along with the private and public keys. You can then use CSNBKRC2 to add it to the local CKDS.

You can do this at the remote end, just switch the public and private, and the importer to exporter. You do not need to send a key between the two systems.

Once you have defined your first exporter and importer key there is an alternative way of creating transport keys, using CSNBKGN2.

Alternative way of defining more transport keys.

There are three scenarios to consider when setting up transport keys between two systems.

  • On system A create an exporter key for system A, and create an importer key system for system B. The importer key will need to be encrypted as it need to be send to the system B. The system A (local) exporter key can then use the key to export a (cipher or MAC) key. Then write this key to a file and send it to the other system. At system B, use the importer key to import this data into the system B’s key store.
  • Do it from the “other end”. On system B create an importer key for system B, and create an exporter key for system A. This exporter key will need to be encrypted as it needs to be send to system A. Then write this key to a file and send it to the other system. At system A import it. System A can then use the new key to export a (cipher or MAC) key from that system, write it a file, and send it to system B which can import it.
  • Define them on another system. On system C create an exporter key for system A, and an importer key for system B. These both need to be encrypted (and use different keys to encrypt each one). Send the encrypted keys to system A and system B. They can import the key, and send keys from system A to System B.

You use CSNBKGN2 to do this. I’ll only cover the first two cases.

You need to specify one of

  • keytype1 = “IMPORTER”, keytype 2 = “EXPORTER” or
  • keytype1 = “EXPORTER”, keytype 2 = “IMPORTER”

Use RULE = “OPEX ” where

  • OP means store the first key in the local (OPerational) key store.
  • EX means keytype 2 is to be exported. You have to specify an AES exporter key in the field key_encrypting_key_identifier_2.

Note. To export the key you already need an AES exporter key! This means you cannot create your first transport key with this method.

The output of this function is

  1. a key which you can add to the local key store using CSNBKRC2,
  2. a key which you can write to a file and send it to the remote system.

At the remote system you use CSNDSYI2 and the Importer key to import the data and re-encrypt it with the local key. Then use CSNBKRC2 to add it to the CKDS.

Which technique is better?

I think using a pair of matching ECC public and private keys and Diffie-Hellman is simpler, as it does not involve sending a file between systems. As this activity is done infrequently it may not matter.

Using DH many not be as secure as the alternative.

How many transport keys do I need?

You could create a new transport key every week if you wanted to. It is only used to send a data key to a remote system, so transient. When you have created a cipher key to encrypt and decrypt your data you need to keep this as long as you need access to the data. Once you recreate it – you are unable to read data sets with the previous key.

Depending on your security requirements you might want to have more than one transport key for data isolation. For example test data and production data have different keys.


You could use Enterprise Key Management Foundation (EKMF) from IBM to manage your keys

EASY ICSF – making it easy to use the API to generate and export/import keys

I’ve put some code on GITHUB which has C and REXX code which have a simpler interface to ICSF. The code examples hide a lot of the complexity.

For example to generate an AES CIPHER key the high level C code is

// build the skeleton for C=CIPHER ( could be E for exporter or I for IMporter
//  It returns the skeleton and its length 
rc = skeletonAES("C",& pToken,& lToken); 
if ( rc != 0 ) return rc; 

// Generate the key - passing the skeleton and returning the Token
// input the skeleton 
// output the token 
rc = GENAES2(pToken,&lToken); 
if ( rc != 0 ) return rc; 

// Add this to the CKDS                                                        
rc = addCKDS(pKey,pToken       ,lToken,pReplace); 
if ( rc != 0 ) return rc; 

printf("GENAES %s successful\n",pKey); 
return rc; 

To export an AES key

// Pass in the name of the AES key pKey
// the name of the encryption key (AES EXPORT or PKI) pKek
// Get back the blob  of data
rc =exportAES (pKey,pKek,&pData, &lData); 
 if (rc > 0 ) return rc; 
Write the blob to a file specified by dd
 rc = writeKey("dd:TOKEN",pData,lData); 

It gives in //SYSPRINT

Exists: CSNBKRR2 read AESDHE CKDS rc 0 rs 0 No error found


KEY:AESDHE:INTERNAL SYMMETRI EXPORTER CANAES


Exists: CSNBKRR2 read PKDS2 CKDS rc 8 rs 10012 Key not found
Exists: CSNDKRR read PKDS2 PKDS rc 0 rs 0 No error found .


KEK:PKDS2:INTERNAL PKA RSAPRIV 1024MEAO


RSA ¬AES:Rule:AES PKOAEP2 SHA-256 AES AESKW AES


ExpAESK:CSNDSYX rc 8 rs 2055 The RSA public key is too small to encrypt the DES key

Where…

  • Exists: CSNBKRR2 read AESDHE CKDS rc 0 rs 0 No error found
    • It used the ICSF CSNBKRR2 to check AESDHE is in the CKDS
  • KEY:AESDHE:INTERNAL SYMMETRI EXPORTER CANAES
    • It reports some info on the key. It is a Symmetric (AES) Exporter and can do AES processing
  • Exists: CSNBKRR2 read PKDS2 CKDS rc 8 rs 10012 Key not found
    • This is ok — it looks in the CKDS first – but as this is a PKI – it will not be found
  • Exists: CSNDKRR read PKDS2 PKDS rc 0 rs 0 No error found .
    • It is found in the PKDS
  • KEK:PKDS2:INTERNAL PKA RSAPRIV 1024MEAO
    • This gives info about the Key Encryption Key. It is RSA and has a private key. The key size is 1024
  • RSA ¬AES:Rule:AES PKOAEP2 SHA-256 AES AESKW AES
    • This is the rule used
  • ExpAESK:CSNDSYX rc 8 rs 2055 The RSA public key is too small to encrypt the DES key
    • The size of the PKI key was too small.
    • As well as giving the return code and reason code, it gives the reason for some of the reason codes.
    • When I repeated this with a RSA key with a large enough key – it worked successfully.

There are also some macros such as

  • isRSAPRIV… is this token (blob of data) an RSA private key?
  • isEXPORTER … has this token been defined as an EXPORTER key?

I use these to check keys being used for operations to check that keys are valid for the ICSF operations.

The git hub code is work in progress. As I find problems I’m fixing them, but overall it should you what you can do with it.

ICSF: why do I need to have transport keys as well as data encryption keys.

As part of my scenario of encrypting a file and sending the encrypted file to another z/OS system, I struggled to understand why the documentation referred to transportation keys, key encryption keys (KEKs), import keys and export keys.

I found the subject very unclear. As I currently see it (and I’ve changed my view several times). You need the matching key on each system. If this is a symmetric key, it is the same key. If you are using PKI, they keys are asymmetric.

How do you get symmetric keys on both systems. I see there are two ways

  1. Generate the same key on both systems This can be done using private and public keys, and a technique called Diffie-Hellman.
  2. Generate a key on one system, and send it securely to the other system. For this you need to securely package the symmetric keys while they are in transit.

I was able to perform the setup and transfer a file securely to another system without the need for these additional keys. What was I missing?

The discussion about transport keys is for the second example where keys are sent over the network. You can use a CIPHER key to encrypt the key. It comes down to can I do it ? Yes. Should I do it ? No (well, maybe not, it depends on the scale of risk).

Within an IT environment the userid administration should be a different team to the systems programmers. This is to prevent any conflict of interest, fraud, and errors. The system programmers cannot give themselves access to sensitive data. In my small company (with just me in it) I have to do sysprog and userid administration.

IBM has similar guidelines for implementing cryptography. For example

  • Separation of the roles and responsibilities. The people who create keys are different from the people who give access to the keys, and from the people who use the keys.
  • Separation of encryption keys based on what they are used for. A key for encrypting datasets should not be used for encrypting a key to send to a remote system. If a data set encryption key is made public, the key-encryption-key should still be secure.

I could provide isolation of keys by having two keys, one is authorised only for data set encryption and the other authorised only for key encryption, but this separation may not be enough.

Creating exporter/importer using the API

I spent a couple of days trying to create an importer/exporter pair. I found one way of doing it – there may be other (more obscure) ways. It uses Diff-Hellman to create the same key on two sites without transferring sensitive material. I describe it here. It requires each side to have its own private key, and the public key of the other side.

There are three parts

  • Generate a skeleton
  • Use the skeleton, private key and public key to generate the Diffi-Hellman key
  • store it in the key store

Exporter:Generate a skeleton

I used CSNBKTB2 with rules ‘AES ‘||’INTERNAL’||’EXPORTER’.

Exporter:Generate the DH Key

I have a “helper” rexx function which has parameters, private key name, public key name, the completed skeleton.

It used CSNDEDH with

  • rule_array = ‘DERIV01 ‘||’KEY-AES ‘
  • party_identifier (a string both sides agree) = ‘COLINS-id’
  • KEK_key_identifier_length = 0. This is used when the private key is not stored in the PKDS, but passed in encrypted. I think of this as acting as a proxy. “Here is the private key to use – but it has been encrypted with the KEK which is in your local key store”. Setting the length to zero says the private key>is< in the local key store. Definitely an advanced topic!
  • Name of side A’s private key in the PKDS
  • Name of side B’s public key (from the other side) in the PKDS
  • key_bit_length = 256.

It returns a blob encrypted with the local master key.

Exporter:addckds

This is another rexx helper. It takes the name of the key to generate, the encrypted blob, and “replace=Y|N”

This uses

  • CSNBKRC2 to add to the CKDS
  • if it gets record found, and needs to delete it,
    • it invokes delckds which uses CSNBKRD to delete it
    • it tries the add again

Importer ( on the remote system)

The steps are the same, except

  • I used CSNBKTB2 with rules ‘AES ‘||’INTERNAL’||’IMPORTER’.
  • Generate the DH key, you use the other keys, side B’s private, and side A’s public.

To export a key using exporter/importer

If you are using an AES exporter key to encrypt the data you need to use CSNDSYX with

  • The name of the key you want to export
  • The label of the AES exporter key
  • rule_array = ‘AES ‘||’AESKW ‘

It returns a blob which you can write to a data set.

To import the key using exporter/importer

read the data into a buffer

Use CSNDSYI2 with

  • rule_array = ‘AES ‘||’AESKW ‘
  • the name of the importer key

It returns a blob of data.

Use the helper addckds passing the new label name, the blob of data, and replace=yes|no.

  • This uses CSNBKRC2 to add the record, with rule_array = ”
  • If the record exists and replace=yes then
    • use delckds with CSNBKRD and rule_array = ‘LABEL-DL’
    • re-add it

To export a key using PKI public/private keys

If you are using an PKI cipher key to encrypt the data you need to use CSNDSYX with

  • The name of the key you want to export
  • The label of the PKI public key
  • rule_array = ‘AES ‘||’PKOAEP2 ‘

To import the key using pki private key

read the data into a buffer

Use CSNDSYI2 with

  • rule_array = ‘AES ‘||’PKOAEP2 ‘, matching the exporter
  • the name of the private key

It returns a blob of data.

Use the helper addckds passing the new label name, the blob of data, and replace=yes|no.

  • This uses CSNBKRC2 to add the record, with rule_array = ”
  • If the record exists and replace=yes then
    • use delckds with CSNBKRD and rule_array = ‘LABEL-DL’
    • re-add it

ICSF: exploiting Rexx

ICSF provides APIs and commands to manage cryptographic keys. For example to encrypt a datasets you need to define the key that will be used.

You can use Rexx to use the API’s and make your own commands.

There are some Rexx samples provided with ICSF, and there are others on the internet if you search for the API function and Rexx. These tend to be a large Rexx exec written to do one function.

You can use the power of Rexx to allow significant reuses of these execs, by having one Rexx exec to generate a key, another Rexx exec to add it to the keystore, another Rexx exec to export it, and another Rexx exec to import it.

Background

Rexx Address linkpgm facility

With TSO Rexx there is the “address linkpgm” command environment. This allows you to call z/OS functions with Rexx parameters.

For example

rc = 0
y=”Mystring”
z= 16
address linkpgm “ZOSPROG myrc Y Z”

generates the standard low level request

call ZOSPROG(addr(myrc),addr(y),addr(z));

It returns a variable ‘RC’ for example -3 if the program is not found, or the return code from the program.

Be careful not to specify ‘RC’ as a parameter as it may override it.

If does what you tell it. If you are expecting a string to be returned, then the variable you give it must be big enough to hold the data, it cannot allocate a bigger string.

If you want to create a variable of a fixed size you can use

token         = copies(’00’x,3500);

If you are passing a number or hex string, you have to convert it to the internal value.

For example on input

myInt = ‘00000000’x
mylen = C2D(length(“ABCDEFG”),4) /* the 4 says make field 4 (int) wide */

on output, convert the hex return code to a readable hex code

myrc = c2x(myrc)

To create an internal format length you can use either of

lToken = ‘00001964’x /* 6500 */
lToken = d2c(6500,4); /* of size 4*/

Passing parameters to external Rexx programs

You can call external Rexx programs and get a returned data. For example

with the program mycode

parse arg a,b
return 0 “COLINS”||A b||”xxx”

and call it using

zz = mycode(“AA”,”B”)
say zz
parse var zz rc x y
say rc
say x
say y

gives

0 COLINSAA Bxxx
0
COLINSAA
Bxxx

Using this you can have an external function which generates an AES key, which returns the return code, reason code and the data.

Using hex strings

Many of the ICSF functions return a hex structure. You can convert this from internal using the Rexx function c2x. This takes a string and creates the hex version of it. When you want to use it in another ICSF function you convert it back again using x2c().

x = ‘ABC’
y = c2x(x)
say ‘y:’y /* gives y:C1C2C3 */

When an ICSF function returns data, you can convert it to the hex string, and return it to the caller.

Using lengths

If a hex length has been returned, you can convert it to a Rexx number using C2D

x = ‘00000000c’x
say ‘x:’c2d(x) /* prints x:12 */

Converting from Rexx to internal format

x = 14
y = d2c(x,4) /* a 4 byte field */
say ‘x:’c2x(y) /* display in hex gives x:0000000E */

Using ICSF from Rexx

Using the program

/*********************************************/ 
/* Generate a 256-bit AES DATA key to export */ 
/*********************************************/ 
rc = genAES() /* this returns several bits of data*/
say "CPBKGN " rc 

parse var rc myrc myrs key 
if myrc <> 0 then return rc 
                                                                         
/********************************************/ 
/* Store the AES DATA key in the CKDS       */ 
/********************************************/ 
/* just return code */ 
rc= addCKDS("REXXLABEL",key) 
say "CPBkrc2" rc 
return 0 

And GENAES

say "In GenAES" 
parse arg a  /* no parameters passed in */ 
/********************************************/ 
/* Generate a 256-bit AES DATA key to export*/ 
/********************************************/ 
key_form               = 'OP  ' 
key_length             = 'KEYLN32 ' 
key_type_1             = 'AESDATA ' 
key_type_2             = '' 
kek_id_1               = COPIES('00'x,64) 
kek_id_2               = '' 
generated_key_id_1 = COPIES('00'x,64) 
generated_key_id_2 = '' 
                                                                   
myrc             = 'FFFFFFFF'x 
myrs              = 'FFFFFFFF'x 
exit_length = d2c(0,4)
exit_data       = '' 
ADDRESS linkpgm "CSNBKGN", 
   'myrc'               'myrs'          , 
   'exit_data_length'   'exit_data'     , 
   'key_form'           'key_length'    , 
   'key_type_1'         'key_type_2'    , 
   'kek_id_1'           'kek_id_2'      , 
   'generated_key_id_1' 'generated_key_id_2' 
 
myrc = c2d(myrc)
myrs = c2d(myrs)                                                                 
IF (myc <> 0 ) THEN 
  DO 
    SAY 'KGN Failed   (rc='myrc' rs='myrs')' 
    Return  myrc myrs 
  END 
                                                                  
Return  myrc myrs c2x(generated_key_id_1)
                                                                         

ADDCKDS

/* -------------------------------------------*/ 
/*  Add CKDS : label and data                 */ 
/* CSNBKRC2 - Key Record Create2              */ 
/* -------------------------------------------*/ 
parse arg label, token 
say "CPBKRC2 " label token 
myrc = 'FFFFFFFF'x 
myrs = 'FFFFFFFF'x 
exit_length =d2c(0,4)
exit_data = '' 
rule_count = d2c(0,4)
rule_array = '' 
token_length = d2c(64,4)
token =x2c(token) 
LEFT(data,64) /* Make sure string length = 64 */ 
ADDRESS LINKPGM "CSNBKRC2", 
   'myrc'          'myrs'            , 
   'exit_length'   'exit_data'       , 
   'rule_count'    'rule_array'      , 
   'label'         'token_length'   , 
   'token'                                                              
myrc = c2d(myrc)
myrs = c2d(myrs)                                                                 
IF (myrc <> 0 ) THEN 
    /* print the return code and description text */
    SAY 'KRC2 Failed   (rc='myrc' rs='myrs')',
             cprs(myrc,myrs)
    RETURN  myrc myrs 
  END 
                                                                 
RETURN   myrc myrs 

and the printable reason code

/* exec to give back reason code string from passed value */ 
parse arg rc,rs 

v.= "Not listed" rs 
v.762="The key values structure for CSNDPKB has a field in error"||, 
            "A length or format is not correct" 
v.2012="The rule_array_count parameter contains a" ||, 
           " number that is not valid." 
v.2016="Rule Array wrong" 
v.2040="Wrong key type.   For example PKI when Importer was expected" 
v.2054="RSA:OAEP optional encoding parameters failed validation" 
v.2089="The algorithm does not match the algorithm"||, 
           " of the key identifier" 
v.10012="Key not found" 
....
return v.rs 

Notes:

I converted from a string to a hex representation of the string when passing data around because the hex data could have a blanks in it. Using the Rexx parse var x a b c parses on blank delimited words, and imbedded blanks could cause a mis-parse.

Understanding ICSF fixed and variable format keys and how not to get confused when using them.

This is part of the project to set up ICSF so I can create an encrypted dataset on one system, and use it on a different system. This involves setting up keys for encryption, public/private keys and sending stuff between the two systems.

Dataset encryption keys are symmetric and reside in the CKDS.

It took me a while to understand the implications of the fixed and variable format keys, and lots of head scratching when things did not work as expected.

This post is a very simplistic view of the topic – but it should give you enough information to get started with ICSF.

One thing you need to know about (and then quickly forget) is that there are two format of keys.

  1. Fixed length – an example of this is a DATA key.
  2. Variable length – an example of this is is a CIPHER key.

I just think of there being two versions of APIs – Version 1 for Data and version 2 for Cipher. Any new project should use variable length keys.

You can use either DATA or CIPHER to encrypt a dataset.

Each type of key has its own APIs, so you cannot use a fixed key in an API designed for variable length keys.

Most ICSF APIs have “rules” which are like passing parameters to a command. This is an array of 8 character strings such as “AES “,”PKCS-1.2”,OP “. This string indicates

  • AES – encrypt an AES keyType
  • PKCS-1.2 using PKCS
  • OP “Operational” for use on this system.

When using the APIs’ to export keys, both ends must have matching configuration

For example with a DATA key any one of the following.

  1. AES,PKCS-12
  2. AES,PKCSOAEP
  3. AES,PKCSOAEP,SHA-512

I think the last set of parameters is the strongest.

For CIPHER, this worked

  1. AES, PKOAEP2, SHA-256

When programming I found it easier to create some helper routines to reduced the complexity of the APIs. For example I created a ADDKEY routine to specify PKDS|CKDS, the key name, and the buffer contents. As a result I had a high amount of reuse, and my main programs were very compact.

Below I give the API calls for

  • Using variable length Cipher keys
    • create the skeleton using CSNBKTB2
    • create the data key using CSNBKGN2
    • export the data key using CSNDSYX
    • import the data key (on another system) using CSNDSYI2
  • Using fixed length data keys
    • create the data key using CSNDSYG
    • export the data key using CSNDSYX
    • import the data key (on another system) using CSNDSYI

Using variable length Cipher keys

I see the variable length keys as an evolution in key management from the fixed length keys.

For example you can store the name of the key within the key (though I do not know when or how this is used).

Create the skeleton

I built a skeleton using CSNBKTB2. You can optionally pass in

  • a key name
  • user data

I did not set these (I set the lengths to zero (&zero)).

I passed in the rule

  • char 8 rule[4] = “INTERNAL”,”AES “,”CIPHER “, “ANY-MODE”

The ANY-MODE was required for the encryption to work.

Create the cipher key

I then created the key using CSNBKGN2 and passed in

  • char8 rule[2] = {“AES “,”OP “};
  • keyLength = 256 ; why use a weaker key?
  • char8 keyType1 = {“TOKEN “}; This says use the data passed in from the skeleton token.
  • char8 keyType2 = {” “};
  • The skeleton data

This returns a block of data (the AES Cipher key encrypted with the local key).

I added it to the local keystore using CSNBKRC2.

Export the cipher key

I exported the key using a public certificate with CSNDSYX. I passed

  • the name of the public key.
  • char8 rule_array[3] = { “AES “,”PKOAEP2 “,”SHA-256 “}

It returns a block of data containing the AES cipher key encrypted with the public key.

I wrote this data to a file (in binary) and sent it to my remote system.

Import the cipher key at the remote system

I read the file into a buffer, and used CSNDSYI2 to decrypt the contents using the private key, and encrypting it with the local key.

I passed

  • char8 rule_array[2] ={“AES “,”PKOAEP2 “}
  • the name of the private key
  • the buffer

it returned a buffer containing the re-encrypted key

I added it to the keystore on the remote system using CSNBKRC2.

__________________________________________________

If you want to use fixed length keys(why do you?)…

Using fixed length data keys

You can create a data key, and export it at a later date, or you can generate it and export it at the same time.

I feel more comfortable about exporting it when it is needed, in case the “old” copy is out of data.

If you export it from the live system you know it is current.

Create the data key

I used use CSNDSYG

with

  • a char 64 key name
  • char8 rule[3] = {“AES “, “PKCS-1.2″| PKCSOAEP, “OP “}; If you use PKCSOAEP you can specify SHA-512

This creates a data key in the local repository.

I think PKCSOAEP is better than PKCS-1.2 as is is more recent.

You can also get it to create an RSA enciphered version of the key using a public certificate (as part of key creation) This can be written to a file, and the file sent to the remote system. I tend not to use this, but export the key at the time when wanted. This way you can be sure you have the correct key.

Export the data key

Use CSNDSYX with similar parameters as for the create (CSNDSYG) to create an encrypted version of the Data key.

CSNDSYX returns a block of storage with the encrypted key in it. You can write this to a file in binary or create a base64 encoding of it.

Import the data key

Use CSNDSYI with

  • The block of encrypted data (which you can read from a file)
  • rule = “AES “, “PKCS-1.2″|| “PKCSOAEP”. If you use PKCSOAEP you can specify SHA-512
  • The parameters must match the sending end
  • The name of the private key in the PKDS to be used to decrypt the data

This returns a block of data which is the data key, encrypted with the local system’s key.

You can then add it to the CKDS using CSNBKRC2

ICSF: My first C application.

As part of setting up data set encryption to send data sets between two sites. I wrote some programs to help securely send the symmetric key exchange between two systems using private and public keys.

Basis flow to get a symmetric key on two systems to allow data set encryption and decryption, using private key and public key.

The steps to allow me to send an encrypted dataset to a remote system, using PKI are as follows

  1. On the remote system generate a private/public key in the PKDS
  2. Extract the public key and send it to the local system
  3. Import the public key into the local PKDS
  4. On the local system, generate a symmetric AES key for encrypting data sets.
  5. Export the key, encrypt it using the public key, into a buffer.
  6. Write the buffer to a file
  7. Send the file to the remote system.
  8. Read the file, decrypt the buffer using the private key, re-encrypt with the local master key, store it in the local CKDS.
  9. Displayed the key at each end and compared the
  10. Use the symmetric key to decrypt a dataset.

The programs

I wrote some helper programs for example read a key from the CKDS returning the data; create an entry in the PKDS with a specified key and a blob of data containing the token.

Create a private public key

I could not find a batch utility to define a private key, but could use the ISPF panels.

I started writing my own program to do this…

I used API function CSNDPKB. I successfully created a Private/Public key using Elliptic Curve (ECC).

I could display this using the ISPF panels 5 UTILITY->6 PKDS KEYS → 1. This lists all of the keys. Use the line command K to display it. ( D is for delete!). It displays attributes like Algorithm: ECC, size 512, Sections: PRIVATE PUBLIC.

I later found out that ECC cannot be used to encrypt a symmetric AESkey. “Use the Symmetric Key Export callable service to transfer an application-supplied AES, DES or variable length symmetric key token key from encryption under a master key to encryption under an application supplied RSA public key or AES EXPORTER key. I could not find how to generate an EXPORTER key with public certificate.

I tried using CSNDPKB to create an RSA private key. You have to complete a “Key Value Structure”. I struggled to complete this for RSA because I needed to specify “prime number p”,and “prime number q”. Ive since found how to do this. I tried an alternate approach of using the ISPF panels.

On the remote system, I used the ISPF panels to create a PKI key. 5 UTILITY → 6 PKDS KEYS → 6 Generate PKA keys. I created a new RSA key with bit length 4096, as bit length 512 was too weak.

Having created my private/public key, I used the ISPF panels to export the public key to a data set, sent it to my local system and import it using the ISPF panels. This public key does not need to be sent securely.

  • You can extract the contents of this file using RACDCERT ADD(‘COLIN.EXPORT.ECC1’) ID(COLIN). This gave me a key with
Certificate ID: 2QXD1tPJ1dPBwsXT8PDw8PDw8PFA
Status: TRUST
Start Date: 2021/08/29 09:42:44
End Date: 2041/08/29 09:42:44
Serial Number:
>00< 
Issuer's Name: >CN=ECCB512Z< 
Subject's Name: >CN=ECCB512Z<
Signing Algorithm: sha256ECDSA
Key Type: Brainpool ECC
Key Size: 512
Private Key: NO
Ring Associations:

I had now had a private key at the remote end, and the public certificate at the local end.

I can encrypt at the local end using the public key, and decrypt it at the remote end using the private key.

Create the AES symmetric key on the local end.

I found I could not use AES type DATA, and so had to use AES type CIPHER.

If I used KGUP utility with

ADD TYPE(CIPHER ) ALGORITHM(AES) LENGTH(32) LAB(AESCIPHER)

This could not be used to encrypt because it gave IEC143I 213-85 RC=X’00000008′, RSN=X’0000085E’

85E (2142) The key usage attributes of the variable-length key token does not allow the requested operation. For example, the request might have been to encrypt data, but encryption is not allowed, or the request might have been to use the ECB cipher mode, but that mode is not allowed.

When I displayed through the ISPF panels (5.5.1) it it showed

Key Usage: ENCRYPT DECRYPT CBC

If I generated an AES key using the API, I found that ENCRYPT DECRYPT ANY-MODE worked, but I could not see how to set ANY-MODE using KGUP.

Program to generate an AES key

 // build the skeleton.   It is returned in pToken
 rc = skeletonAES(&pToken,& lToken); 
 if ( rc != 0 ) return rc; 
 
 // input: the skeleton 
 // output: the token 
 rc = GENAES2(pToken,&lToken); 
 if ( rc != 0 ) return rc; 

 // add it to the CKDS                                                           
 rc = doAdd2("C",pKey,pToken,lToken,pDelete); 
 if ( rc != 0 ) return rc; 
 return rc; 

skeletonAES program

// this program allocates storage, uses it, and passes it back
// to the caller. 
int skeletonAES(char ** pData, int * lData) 
 { 
  int rc; 
  int rs; 
  int zero = 0; 
  int rule_count                 =  4 ; 
  char8 rule[ 4] ={ 
   "INTERNAL","AES     ","CIPHER  ",    "ANY-MODE" 
   }; 
  char * pRule = & rule[0][0];
  int lKeyToken=725; 
  char * pKeyToken; 
  pKeyToken = (char *) malloc(lKeyToken); 
  CSNBKTB2(   
           &rc,         /* return code             */ 
           &rs,         /* reason code             */ 
           &zero,       /* exit data length        */ 
           0,           /* exit data[]             */ 
           &rule_count, /* rule array count        */ 
           pRule,       /* rule array[]            */ 
           &zero,       /* clear key bit length    */ 
           0,           /* key value[]             */ 
           &zero,       /* key name length         */ 
           0,           /* key name[64]            */ 
           &zero,       /* user assoc data length  */ 
           0,           /* user associated data[]  */ 
           &zero,       /* token data length       */ 
           0,           /* token data[]            */ 
           &zero,       /* service data length     */ 
           0,           /* service data[]          */ 
           &lKeyToken,  /* target key token length */ 
           pKeyToken ); /* target key token[]      */ 
 if ( rc > 0) 
 { 
    printf("CSNBKTB2 rc %i rs %i %s\n",rc,rs,csfgetrc(rs)); 
    return rc; 
 } 
 
   free (pData); // get rid of the passed in block 
// return the length
 *lData = lKeyToken;
//pass back the address of the token
 *pData = pKeyToken; 
 printf("skeletonAES key length %i\n",lKeyToken); 
// printHex(stdout,&keyToken,lKeyToken); 
   printAES((char *) pKeyToken,lKeyToken); 
   return 0; 
} 

Create program keygenerate

// this takes the data passed in, and uses it
// The length is updated.
int keyGenerate2( char * pData, int *  lData) 
{ 
  int rc; 
  int rs; 
  int zero = 0; 
  int rule_count                 =  2; 
  // key type AES and used for OP (on this system 
  char8 rule[2]  = {"AES     ","OP      "}; 
  char * pRule = &rule[0][0];
  int keyLength = 256 ;//  AES 256  - why use any other? 
                                                                            
  char8  keyType1 = {"TOKEN   "}; 
  char8  keyType2 = {"        "}; 
  int i64 = 64; 
  int l725 = 725; 

 CSNBKGN2( 
         &rc  ,        /* return code             */ 
         &rs  ,        /* reason code             */ 
         &zero,        /* exit data length        */ 
         0,            /* exit data[]             */ 
         &rule_count,  /* rule array count        */ 
         pRule,        /* rule array[]            */ 
         &keyLength,   /* clear key bit length    */ 
(char *) &keyType1   , /* key type1[8]            */ 
(char *) &keyType2   , /* key type2[8]            */ 
         &zero,        /* key name1 length        */ 
         0,            /* key name1[64]           */ 
         &zero,        /* key name2 length        */ 
         0,            /* key name2[64]           */ 
         &zero,        /* user assoc data1 length */ 
         0,            /* user associated data1[] */ 
         &zero,        /* user assoc data2 length */ 
         0,            /* user associated data2[] */ 
         &zero,        /* KEK id1 length          */ 
         0,            /* KEK id1[]               */ 
         &zero,        /* KEK id2 length          */ 
         0,            /* KEK id2[]               */ 
         &l725,        /* output key id1 length   */ 
 (char *)pData,        /* output key id1[]        */ 
         &zero    ,    /* output key id2 length   */ 
         0          ); /* output key id2[]        */ 
  if ( rc > 0) 
  { 
    printf("CSNBKGN2 rc %i rs %i %s\n",rc,rs,csfgetrc(rs)); 
    return rc; 
  } 
  *lData = l725; 
  // the same buffer is used,so no need to set pData
  return rc; 
} 

Using these and my add2(…) functions I could create my AES for CIPHER with ANY-MODE. (See ANY-MODE above).

Export the AES key

To export the AES key, you need the name of the public key.

You need a different set of rules for a DATA key and a CIPHER key.

 int   lData; 
 char * pData; 
 rc =doExportAES (pKey,pPublic,pType,&pData, &lData); 
 if (rc > 0 ) return rc; 
 rc = writeKey(dd,pData,lData); 
 if (rc > 0 ) return rc; 

I created a routine which read the key, and one to write it to a file.

The parameters to doExportAES are

  • dd name “dd:CERT would use //CERT .. in the JCL
  • key is a char64 left justified key name in the CKDS
  • pPublic a char 64 left justified name of the public key to use. It must exit in the PKDS.
  • A pointer to the string to containing the data
  • The length of the returned data.
int doExportAES (char * pKey, char * pPublic, char * pType, 
                 char ** pData, int * lData)  
{ 
  int rc; 
  int rs; 
  int i64 = 64; 
  int zero = 0; 
  int rule_count                 =  3; 
  char8 rule_Cipher[3] = 
                     {"AES     ", 
                      "PKOAEP2 ", 
                      "SHA-256 "}; 
  char * pRule =   rule_Cipher[0][0];
  int lOut = 900; 
  char * pOut; 
  pOut = (char * ) malloc(lOut); 
  printf("Source Key user   %64.64s.\n",key  ); 
  printf("Transport key     %64.64s.\n",encrypt); 

   CSNDSYX ( 
            &rc,                /* return code            */ 
            &rs ,               /* reason code            */ 
            &zero,              /* exit data length       */ 
            0    ,              /* exit data[]            */ 
            &rule_count,        /* rule array count       */ 
            pRule,              /* rule array[]           */ 
            &i64   ,            /* source key    length,  */ 
            pKey  ,             /* source key             */ 
            &i64,               /* RSA public key length  */ 
            pPublic,            /* RSA public key token   */ 
            &lOut,              /* exported key length    */ 
            pOut           );   /* exported key           */ 

if( rc > 0) 
  printf("CSNDSYX  rc %i rs %i %s\n",rc,rs,csfgetrc(rs)); 
if (rc != 0) return rc; 

// return the exported data

 *pData = pOut; 
* lData = lOut; 
   return 0; 
} 

Import the certificate at the remote end

I created IMPAES

  rc=  read(dd,&pData,&lData);         // returns a buffer  from the file
  printf("readCert rc %i data length %i\n",rc,lData ); 
  if ( rc != 0) return 8; 

  rc = doImportAES(pKey,pPrivateKey,&pData,&lData); 
  if ( rc != 0) return 8; 
  printf("Returned buffer size %i\n",lData); 
  rc = doAdd2("C",  pKey,pData,lData); 
  if ( rc != 0 ) return rc; 
  return 0; 

read opens the ddname , and gets back the (binary) data in pData of length lData.

This get passed to doImportAES which uses the private key pointed to by pPrivateKey (a 64 char left justified string). This then returns the AES key (encrypted with the local key) which is then added using do ADD2.

DoImportAES.

/* --------------------------------------------------------------- *
/* Import public key                                               *
/* --------------------------------------------------------------- *
int doImportAES (char * pKey, char * pDecryptKey, 
    char ** pData, int * lData) 
{ 
  int rc; 
  int rs; 
  int zero = 0; 
  int i64 = 64; 
  int rule_count                 =  2; 
  char8 rule_array[2]            ; 
  memcpy(&rule_array[0],"AES     ",8); 
  memcpy(&rule_array[1],"PKOAEP2 ",8); 
  int lOutput = 725 ; 
  char * pOutput = (char * ) malloc(lOutput); 
  if ( pOutput == 0) 
  { 
    printf("malloc for %i failed in IMPAES\n",lOutput); 
  } 
  printf("Key %64.64s.\n",pKey); 
  printf("Decrypt Key %64.64s.\n",pDecryptKey); 
  printf("lInput  %i\n",*lData     ); 
//printHex(stdout,* pData,32); 
  CSNDSYI2 ( 
              &rc,           /* return code            */ 
              &rs ,          /* reason code            */ 
              &zer           /* exit data length       */ 
              0    ,         /* exit data[]            */ 
              &rule_count,   /* rule array count       */ 
   (char *)   &rule_array[0],/* rule array[]           */ 
              lData     ,    /* length of input data   */ 
            * pData,         /* input data             */ 
              &i64,          /* cca DES token length,  */ 
   (char *)   pDecryptKey ,  /* cca DES token[64]      */ 
              &i64,          /* key name length        */ 
   (char *)   pKey,          /* key name[]             */ 
              &lOutput    ,  /* Imported key length    */ 
   (char *)   pOutput   );   /* Imported key           */ 
// if( rc > 0) 
   printf("CSNDSYI2 rc %i rs %i %s\n",rc,rs,csfgetrc(rs)); 
 if (rc != 0) return rc; 

 // get rid of input buffer 
   printf("CSNDSYI2 data lengt %i\n",lOutput); 
   free (*pData); 
// and update with the new data
   *pData =  pOutput; 
   *lData = lOutput; 
 return 0; 
 } 

All this creates a key in the CKDS on the remote system which I could use to decrypt a data set.

Summary

It took a long time to write these programs, because I did not know the path to take, and went down many dead ends. Once you know the concepts and know which ICSF functions and options you need, it is not too difficult.

Having these helper routines, I was able to create a program to generate a symmetric key using Diffi-Hellman in about 2 hours! (Most of this time was reading the documentation).