Using ICSF API function CSNBKGN2 to generate keys

The ICSF function CSNBKGN2 allows you to generate keys for encrypting data (such as data sets) where you can

  • specify the hex data for the key value
  • have ICSF generate the key value securely so that the key value is never exposed as clear text.

Once a key has been created the key value will either be encrypted with the hardware key of the cryptographic hardware the program is running on, or is encrypted with Key Encryption Key, so it can be securely sent to a remote system.

You can generate a key in two modes:

  1. For use on the local system. This is known as the OP mode, because the cipher key is OPerational immediately. You can use it immediately to encrypt data, or you can use another ICSF function to write the key data to the CKDS. Once it has been written to the CKDS, you can refer to and use it by its label. At a later date you can extract the key and encrypt with another key for sending to a remote system.
  2. Encrypted under a transport key, called a Key Encrypting Key (KEK) an EXporter or IMporter key). You can write the created key to a file, send the file to a remote system, and then use an ICSF function(CSNDSYI2) to (decrypt it and re-encrypt it with the hardware key) and then write it to that systems CKDS. This is mode EX or IM.

The documentation for CSNBKGN2 has information about all of the options, but it is not very clear on how to use it!

I found it easier to understand the concepts, and to use, if I used two steps; generate a cipher key and then export it from the CKDS using a KEK, rather than trying to generate a cipher key for local use and encrypt it with a KEK in a single API call (point 3. below).

The CSNBKGN2 function has several modes:

  1. Generate a cipher key which you can use to encrypt data. This is known as the OP mode, because the cipher key is OPerational immediately.
  2. Generate a cipher key which is encrypted with a Key Encrypting Key. This is mode EX or IM.
  3. Generate a key you can use to encrypt data – both in OP mode, and generate the same key encrypted with a KEK, so you can send it somewhere. This is a combination of 1. and 2. in one API request (You specify mode = OPEX or mode = OPIM).
  4. Generate an exporter/importer pair of keys for encrypting other keys.
    • One of the pair is (usually) created so it can be used immediately, or written to the CKDS. This is known as the OP mode.
    • The other is encrypted with an existing Key Exporting Key. This is mode EX or IM.
    • The combined mode is OPEX or OPIM.

CSNBKGN2 provides some defaults which are suitable for some keys, but not suitable for AES CIPHER keys used to encrypt data sets.

You can create a skeleton key using CSNBKTB2, to specify non default parameters, and get CSNBKGN2 to use the skeleton. If you want to restrict what a key can do, you will need to use the skeleton. For example you can say this exporter/importer key pair can be used to encrypt AES keys, but not DES keys. For more information see the documentation. You tell CSNBKGN2 to use a skeleton by using the TOKEN keyword instead of CIPHER, EXPORTER, or IMPORTER.

Guidance on using CSNBKGN2

Lengths of data

When using some values such as KEKs, you can specify a key name in the CKDS of length 64 bytes. You can also read the key token from CKDS yourself (or get the key token from somewhere else). In this case the length you specify of the KEK may be ignored and the length of the data in the key token is used.

If an ICSF function provides you a key token, it will check your buffer is big enough, and return the length it used.

The function complains if you give a length field more than the maximum value. So when the documentation says generated_key_identifier_2_length is the maximum value is
900 bytes.
You must provide an area of the specified size, because it will complain if a different size is used.

Terms

The documentation uses the term generator key identifier. I think of this as the skeleton input.

My basic use of CSNBKGN2

Because of many parameters, I’ll give an example of my “standard” usage. The documentation gives the syntax

CALL CSNBKGN2(
return_code,
reason_code,
exit_data_length,
exit_data,
rule_array_count,
rule_array,
clear_key_bit_length,
key_type_1,
key_type_2,
key_name_1_length,
key_name_1,
key_name_2_length,
key_name_2,
user_associated_data_1_length,
user_associated_data_1,
user_associated_data_2_length,
user_associated_data_2,
key_encrypting_key_identifier_1_length,
key_encrypting_key_identifier_1,
key_encrypting_key_identifier_2_length,
key_encrypting_key_identifier_2,
generated_key_identifier_1_length,
generated_key_identifier_1,
generated_key_identifier_2_length,
generated_key_identifier_2 )f

Some constants I frequently use.

int rule_count = 2;
char * pKeyType1 = "EXPORTER";
char * pKeyType2 = "IMPORTER";
int lKey1 = 64;
int lKey2 = 64;
char KeyName1[64] ...
char KeyName2[64] ...
int lKekName = 64;
char KekName[64] ....
int lData1 = 900;
char data1[lData1];
char * pData1 = &data1[0];
int lData2 = 900;
char data2[lData2];
char * pData2 = &data2[0];

I used the parameters to CSNBKGN2 to define an Exporter/Importer

  • Return code &rc,
  • Reason code &rs,
  • exit data length 0
  • exit data array 0. This is not used because the previous field gives the length as 0.
  • &rule_count, this had the value 2 because pRule below pointed to two parameters.
  • pRule this pointed to {AES, OPEX}. AES is the type of cipher; OPEX says generate the first one so it can be written to the local CKDS, EX says use the second Key Exporting Key – which has a type of EXPORTER.
  • &keyBitLength keyBitLength has the value 256. This specifies how secure to make the cipher.
  • pKeyType1 where KeyType1 was EXPORTER.
  • pKeyType2 where KeyType2 was IMPORTER.
  • &lKey1 value 64. This is the length of the string following – the value of the key name stored in the first key. Set this to zero if you do not want a name stored in the first generated key.
  • &KeyName1[0], the address of the 64 char name to be stored in the first key. You can specify 0 if the length value is 0. This name becomes part of the key data. It needs to be unique within the CKDS.
  • &lKey2 value 64. This is the length of the string following – the value of the key name stored in the second key. Set this to zero if you do not want a name stored in the second generated key
  • &KeyName2[0], the address of 64 char name to be stored in the second key. You can specify 0 if the length value is 0.
  • 0, user associated data length for the first key.
  • 0 not used if the length is zero.
  • 0, user associated data length for the second key.
  • 0 not used if the length is zero.
  • 0,
  • – length of the KEK for the first key. Because the rule was OPEX – the first key is to be stored in the local CKDS and so does not have a KEK.
  • 0 this KEK is not used.
  • &lKekName lKekName has the value 64.
  • &KEK[0] this is the address of the first character of the Key Encrypting Key. It must be padded with blanks to 64 characters.
  • &lData1 the value 900 which is the length of the storage pointed to by pData1 for the first generated key. This storage is 900 characters long. If a smaller value is used, I got rc 8 rs 11000 Invalid length for a key token, key, or text field even though the returned data was less than 120 bytes long.
  • pData1 pointer to the first skeleton, which is also the area where the first key is returned. This storage is 900 characters long.
  • &lData2 the value 900, which is the length of the storage pointed to by pData2 for the second generated key. This storage is 900 characters long. The length of the data is returned here. If a smaller value is used, I got rc 8 rs 11000 Invalid length for a key token, key, or text field even though the returned data was less than 120 bytes long.
  • pData2 pointer to the second skeleton, which is also the area area where the second key is returned. This storage is 900 characters long.

Generate a CIPHER key using Key Generate 2 (CSNBKGN2) directly

I used the following parameters in CSNBKGN2. (See below for all of the typical parameters I specify.) The character values have been formatted for display, they are 8 byte characters, padded on the left with blanks.

  1. rule = {“AES”,”OP” }
  2. KeyType1 = “CIPHER”
  3. keyBitLength 256
  4. kek lengths = 0; no KEK is used (because it will be used/stored on this system)

This created a key, but I could not use it because it defaults to Key Usage CBC, when ANY_MODE is required for data set encryption.

Generate a CIPHER key using a skeleton and Key Generate 2

You need to use Key Token Build CSNBKTB2 if you want to use any of the non default options. For example

Key Token Build CSNBKTB2 parameters

  1. rule_count = 5;
  2. rule = {“EXTERNAL”,”AES “,”CIPHER “, “XPRTCPAC”,”ANY-MODE” }

The “XPRTCPAC”,”ANY-MODE” parameters are required if data set encryption is used

CSNBKTB2 returns an internal format key, which can be used by CSNBKGN2.

CSNBKGN2 parameters

  1. rule_count = 2;
  2. rule = {“AES “,”OP “};
  3. keyType1 = “TOKEN”. This says use the skeleton value in the generated_key_identifier_1 field.
  4. kek lengths = 0; no KEK is used (because it will be used/stored on this system).
  5. generated_key_identifier_1_length 900 (in this case).
  6. generated_key_identifier_1 address of my skeleton data.
  7. generated_key_identifier_2_length 0 – we are not generating a second parameter.
  8. generated_key_identifier_2 0 – this value is ignored.

Generation two CIPHERs one for local, one for remote.

To generate the skeletons

  1. Rule_count = 5;
  2. Rule1 = {“INTERNAL”,”AES “,”CIPHER “, “XPRTCPAC”,”ANY-MODE” }
  3. Rule2 = {“EXTERNAL”,”AES “,”CIPHER “, “XPRTCPAC”,”ANY-MODE” }

CSNBKGN2 parameters

  1. pOPRule = {“AES”,”OPIM “};
  2. keyType1 = “TOKEN”. This says use the skeleton value in generated_key_identifier_1. Skeleton 1 was defined with CIPHER.
  3. keyType2 = “TOKEN”. This says use the skeleton value in generated_key_identifier_2. Skeleton 2 was defined with CIPHER.
  4. kek length1 = 0; no KEK is used (because it will be used/stored on this system).
  5. kek length2 = 64 ; A KEK is used. The name of an IMPORTER KEK.
  6. generated_key_identifier_1_length 900
  7. generated_key_identifier_1 address of my skeleton data, and where the data is returned.
  8. generated_key_identifier_2_length 900.
  9. generated_key_identifier_2 address of my skeleton data, and where the data is returned.

I created one OPerational copy which I wrote to the local PKDS, and an encrypted copy which I wrote to a data set using fwrite(). On the remote system I used fread() toread the file, and used the IMPORTER key to import it into the PKDS. This is the OPIM in the rules.

Generating an importer/exporter pair

In the examples above they just generated a CIPHER for the local system. You can use CSNBKGN2 to generate a key an IMPORTER/EXPORTER pair.

It creates one of the keys in a form that can be added directly to the CKDS, and creates the opposite key, encrypted by a KEK. This means you can write it to a dataset, send the data set to the remote system, and import it using the KEK’s importer key.

To generate the skeletons

  1. Rule_count = 3;
  2. Rule1 = {“INTERNAL”,”AES “,”IMPORTER” }
  3. Rule2 = {“EXTERNAL”,”AES “,”EXPORTER” }

CSNBKGN2 parameters

  1. pOPRule = {“AES”,”OPEX”};
  2. keyType1 = “TOKEN”. This says use the skeleton value. Skeleton 1 was defined with EXPORTER.
  3. keyType2 = “TOKEN”. This says use the skeleton value. Skeleton 2 was defined with IMPORTER
  4. kek length1 = 0; no KEK is used (because it will be used/stored on this system).
  5. kek length2 = 64 ; A KEK is used. The name of an EXPORTER KEK.
  6. generated_key_identifier_1_length 900
  7. generated_key_identifier_1 address of my skeleton data, and where the data is returned.
  8. generated_key_identifier_2_length 900.
  9. generated_key_identifier_2 address of my skeleton data, and where the data is returned.

I used the ICSF function CSNBKRC2 to write the first generated key to the CKDS.

I wrote the second generated key to a file using fwrite() . I could send this file to a remote site, then use CSNDSYI2 with the IMPORTER version of the KEK to process the data into a key for local use, then write it to the CKDS using CSNBKRC2.

Which options produce what output?

  • With EX in the rule OPEX, and an EXPORTER Key Exporting Key it worked.
  • With IM in the rule OPIM, and an EXPORTER key, I got Key not found, because it was searching for keyname of type IMPORTER, when only key name with type EXPORTER was present.
  • With IM in the rule OPIM, and an IMPORTER key, I got reason code 10040 Key identifiers contain a version number.
  • You can use OPIM for CIPHER keys, but not IMPORTER or EXPORTER. See the list of valid combinations.

Setting up ICSF on z/OS

ICSF provides many functions. One function is to manage keys encryption and decryption.

As well as providing encryption etc, it can provide tokens to access encryption keys.

  • ICSF public and private keys are stored in the Public Key Data Set (PKDS)
  • ICSF Cryptographic keys that are encrypted are stored in the Cryptographic Key Data Set(CKDS)
  • ICSF PKCS #11 tokens and objects are stored in Token Key Data Set

There is a batch interface for some command, and ISPF interface using @ICSF, and an API.

The instructions below are a “get you going”. You can protect you keys see Maintaining cryptographic keys.

Create the datasets

Create the CKDS

//IBMCKDS   JOB 1 
//DEFINE EXEC PGM=IDCAMS,REGION=4M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE 'COLIN.SCSFCKDS'
DEFINE CLUSTER (NAME(COLIN.SCSFCKDS) -
VOLUMES(C4SYS1) -
RECORDS(200 100) -
RECORDSIZE(372,32756 ) -
KEYS(72 0) -
FREESPACE(10,10) -
SHAREOPTIONS(2,3)) -
DATA (NAME(COLIN.SCSFCKDS.DATA) -
BUFFERSPACE(100000) -
ERASE) -
INDEX (NAME(COLIN.SCSFCKDS.INDEX))
/*

Create the PKDS

//IBMPKDS   JOB 1 
//DEFINE EXEC PGM=IDCAMS,REGION=64M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE 'COLIN.SCSFPKDS' PURGE
DEFINE CLUSTER (NAME(COLIN.SCSFPKDS) -
VOLUMES(C4SYS1) -
RECORDS(100 50) -
RECORDSIZE(800,32756) -
KEYS(72 0) -
FREESPACE(0,0) -
SHAREOPTIONS(2,3)) -
DATA (NAME(COLIN.SCSFPKDS.DATA) -
BUFFERSPACE(100000) -
ERASE -
CISZ(32768)) -
INDEX (NAME(COLIN.SCSFPKDS.INDEX))
/*

Set up the TKDS

This is described here.

The documentation describes 2 ways of creating the TKDS.

  • Define
  • Define and initialise it

I do not know what the difference is. The first seems to work OK

//IBMCKDS   JOB 1 
//DEFINE EXEC PGM=IDCAMS,REGION=4M
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE 'COLIN.SCSFTKDS'
DEFINE CLUSTER (NAME(COLIN.SCSFTKDS) -
VOLUMES(C4SYS1) -
RECORDS(100 50) -
RECORDSIZE(2200,32756) -
KEYS(72 0) -
FREESPACE(0,0) -
SPANNED -
SHAREOPTIONS(2,3)) -
DATA (NAME(COLIN.SCSFTKDS.DATA) -
BUFFERSPACE(100000) -
CONTROLINTERVALSIZE(32768) -
ERASE) -
INDEX (NAME(COLIN.SCSFTKDS.INDEX))
/*
/*

Update the parmlib member for ICSF

The ICSF job output has information like

IEE252I MEMBER CSFPRMCP FOUND IN USER.Z31A.PARMLIB

Either use this member, or create a new member.

My member has

CKDSN(COLIN.SCSFCKDS)                        
PKDSN(COLIN.SCSFPKDS)
TKDSN(COLIN.SCSFTKDS)
DOMAIN(0)

See Parameters in the installation options data set.

Running on zPDT and ADCD, I also had to specify SSM(YES) so I could specify KGUP parameter CLEAR.

Check your IKJTSOxx has

AUTHPGM NAMES(          /* AUTHORIZED PROGRAMS      */      
...
CSFDAUTH /* ICSF COMMAND */ +
CSFDPKDS /* ICSF Command */
...
)

Started task member CSF

In the ICSF documentation the started task is called CSF. Personally I would have called it ICSF.

//CSF  PROC PRM=CP 
//CSF EXEC PGM=CSFINIT,PARM=&PRM,REGION=0M,TIME=1440,MEMLIMIT=NOLIMIT

RACF Started definition

On my system it uses the default STARTED * (G) profile. This has

STDATA INFORMATION          
------------------
USER= START1
GROUP= SYS1
TRUSTED= NO
PRIVILEGED= NO
TRACE= YES

RACF user definitions

RDEFINE CSFSERV CSF* UACC(NONE) 
RDEFINE CSFSERV CSF1TRL UACC(NONE)
RDEFINE CSFSERV CSF1TRC UACC(NONE)
PERMIT CSF* CLASS(CSFSERV) ID(PKISRVD) ACCESS(READ)
PERMIT CSF1TRL CLASS(CSFSERV) ID(PKISRVD) ACCESS(READ)
PERMIT CSF1TRL CLASS(CSFSERV) ID(COLIN ) ACCESS(READ)
PERMIT CSF1TRL CLASS(CSFSERV) ID(IBMUSER) ACCESS(READ)
PERMIT CSF1TRC CLASS(CSFSERV) ID(IBMUSER) ACCESS(READ)
SETROPTS CLASSACT(CSFSERV) RACLIST(CSFSERV)
SETROPTS RACLIST(CSFSERV,CRYPTOZ) REFRESH

Start ICSF

S  CSF

This gave me

CSFO0230 CKDSN(COLIN.SCSFCKDS)                                
CSFO0230 PKDSN(COLIN.SCSFPKDS)
CSFO0230 TKDSN(COLIN.SCSFTKDS)
CSFO0230 DOMAIN(0)
CSFO0166 DEFAULT CICS WAIT LIST WILL BE USED.
IEE252I MEMBER CTICSF00 FOUND IN SYS1.PARMLIB
CSFM607I A CKDS KEY STORE POLICY IS NOT DEFINED.
CSFM607I A PKDS KEY STORE POLICY IS NOT DEFINED.
CSFM610I GRANULAR KEYLABEL ACCESS CONTROL IS DISABLED.
CSFM611I XCSFKEY EXPORT CONTROL FOR AES IS DISABLED.
CSFM611I XCSFKEY EXPORT CONTROL FOR DES IS DISABLED.
CSFM612I PKA KEY EXTENSIONS CONTROL IS DISABLED.
CSFM654I KEY ARCHIVING USE CONTROL IS DISABLED.
CSFM723I ARCHIVED KEY USE DATA DECRYPTION CONTROL IS DISABLED.
CSFM732I MASTER KEY ENTRY UTILITY KEY OWNERSHIP CONTROL IS DISABLED.
CSFM653I CKDS LOADED 3 RECORDS WITH AVERAGE SIZE 254
CSFM653I TKDS LOADED 45 RECORDS WITH AVERAGE SIZE 2083
CSFM129I MASTER KEY DES ON CRYPTO EXPRESS8 COPROCESSOR 8C00, SERIAL
NUMBER 93AAC740, IS CORRECT.
CSFM129I MASTER KEY AES ON CRYPTO EXPRESS8 COPROCESSOR 8C00, SERIAL
NUMBER 93AAC740, IS CORRECT.
CSFM129I MASTER KEY RSA ON CRYPTO EXPRESS8 COPROCESSOR 8C00, SERIAL
NUMBER 93AAC740, IS CORRECT.
CSFM129I MASTER KEY ECC ON CRYPTO EXPRESS8 COPROCESSOR 8C00, SERIAL
NUMBER 93AAC740, IS CORRECT.
CSFM111I CRYPTOGRAPHIC FEATURE IS ACTIVE. CRYPTO EXPRESS8 COPROCESSOR
8C00, SERIAL NUMBER 93AAC740.
CSFM129I MASTER KEY DES ON CRYPTO EXPRESS8 COPROCESSOR 8C01, SERIAL
NUMBER 93AAC741, IS CORRECT.
CSFM129I MASTER KEY AES ON CRYPTO EXPRESS8 COPROCESSOR 8C01, SERIAL
NUMBER 93AAC741, IS CORRECT.
CSFM129I MASTER KEY RSA ON CRYPTO EXPRESS8 COPROCESSOR 8C01, SERIAL
NUMBER 93AAC741, IS CORRECT.
CSFM129I MASTER KEY ECC ON CRYPTO EXPRESS8 COPROCESSOR 8C01, SERIAL
NUMBER 93AAC741, IS CORRECT.
CSFM111I CRYPTOGRAPHIC FEATURE IS ACTIVE. CRYPTO EXPRESS8 COPROCESSOR
8C01, SERIAL NUMBER 93AAC741.
CSFM133I THERE ARE NO ACTIVE PKCS11 COPROCESSORS.
CSFM508I CRYPTOGRAPHY - THERE ARE NO CRYPTOGRAPHIC ACCELERATORS ONLINE.
CSFM698I DOMAIN IN USE: 0
CSFM015I FIPS 140 SELF CHECKS FOR PKCS11 SERVICES SUCCESSFUL.
CSFM400I CRYPTOGRAPHY - SERVICES ARE NOW AVAILABLE.
CSFM130I CRYPTOGRAPHY - DES SERVICES ARE AVAILABLE.
CSFM130I CRYPTOGRAPHY - RSA SERVICES ARE AVAILABLE.
CSFM130I CRYPTOGRAPHY - ECC SERVICES ARE AVAILABLE.
CSFM127I CRYPTOGRAPHY - AES SERVICES ARE AVAILABLE.
CSFM126I CRYPTOGRAPHY - FULL CPU-BASED SERVICES ARE AVAILABLE.
CSFM001I ICSF INITIALIZATION COMPLETE
CSFM640I ICSF RELEASE FMID=HCR77E0.
CSFM716I ICSF HAS BEEN INITIALIZED WITH SCSFMOD0 FROM LNKLST
CSFM716I ICSF HAS BEEN INITIALIZED WITH SIEALNKE FROM LNKLST

Message CSFM133I THERE ARE NO ACTIVE PKCS11 COPROCESSORS is because zPDT does not support this.

To stop ICSF use the operator command

p CSF

Initialise the environment

Get into the ICSF environment.

  • From ISPF option 6 (TSO)
  • @ICSF
  • panelid on to display the name of the panel in the top left corner of the screen
  • you should be in panel CSF@PRIM
  • option 6 PPINIT – Pass Phrase Master Key/KDS Initialization -> panelid CSFPMC40
  • Enter your pass phrase, tab down to _ Reinitialize system and select it using /
  • Enter your data set names for example CKDS ===> ‘COLIN.SCSFCKDS’ . Ensure the name is in quotes.
  • press enter. I got a panel with message ‘ARE YOU SURE YOU WISH TO PROCEED WITH PASS PHRASE INITIALIZATION?’
  • press enter to continue.
  • The message CSFM653I PKDS LOADED 6 RECORDS WITH AVERAGE SIZE 583 was displayed and INITIALIZATION COMPLETE was displayed in the top right corner.

Update your backup procedure to backup these datasets.

What can you do once it has started?

You can issue operator commands

d icsf,kds                                            
CSFM668I 14.33.27 ICSF KDS 319
CKDS COLIN.SCSFCKDS
FORMAT=KDSR SYSPLEX=N MKVPs=DES AES
DES MKVP date=2024-09-10 12:43:29
AES MKVP date=2024-09-10 12:43:29
PKDS COLIN.SCSFPKDS
FORMAT=KDSR SYSPLEX=N MKVPs=RSA ECC
RSA MKVP date=2024-09-10 12:43:30
ECC MKVP date=2024-09-10 12:43:30
TKDS COLIN.SCSFTKDS
FORMAT=KDSRL SYSPLEX=N MKVPs=None

You can use the @ICSF TSO command (see above)

  • To list the CKDS use 5 UTILITY; 5 CKDS KEYS;1 List all records
  • To list the PKDS use 5 UTILITY; 6 PKDS KEYS;1 List all records
  • To list the TKDS use 5 UTILITY; 7 tokens in TKDS ;4 List existing tokens

Note: Be careful when using the ISPF line commands as the D means delete, not display!

Batch programs

You can use The Key Generation Utility Program CSFKGUP. This uses definitions described here. See One minute MVS – Using individual data set encryption on z/OS for an example of defining a key.

Whoops

After I had used PKI Services to define certificates, I got the health checker message CSFH0054I Check for clear keys in the CKDS, PKDS, and TKDS.

The problems were

Active TKDS: COLIN.SCSFTKDS 
---------------------------------------------
PKISRVD.PKITOKEN 00000001T
PKISRVD.PKITOKEN 00000002T
PKISRVD.PKITOKEN 00000003T
PKISRVD.PKITOKEN 00000004T

Which came from PKISERVD

I fixed this changing the PKI Server pkiserv.conf file to have SecureKey=T, but zPDT does not support encrypted keys, so with zPDT you have to have SecureKey=F.
I got messages in the PKISERVD log

IKYC010I Error 791740530 returned from …: PKI Services can not generate certificates with secure keys.

Setting up ICSF security for commands and APIs

The documentation for ICSF and securing access to keys is hard to follow. There is a lot of it, but is not easily consumed.

There are two orthogonal aspects of ICSF security

  • Giving permission for a userid to use CSFKGUP utility, the ICSF ISPF panels, or the ICSF API. For example they can use list, but not define.
  • Give permissions to groups of keys. You can define keys with names like DSENCR* have more controls that ANYONE*

There are different levels of granularity for ICSF security.

  • The default is no security – you need to define profiles to protect ICSF resources
  • There is on|off security. If you have any security you can do anything
  • Then there is granular security. You can control down to the operation type.

This blog post is about setting up security for ICSF before you start defining keys.

ICSF security for commands and APIs

The approach to security for ICSF seems to be every one has access unless you take action to define security!

The CSFKGUP utility.

There is a master switch – if this profile exists then do more checks. If this profile does not exist any userid can use CSFKGUP commands to maintain keys. You need to set up Optional SAF checking for KGUP.

RDEFINE XFACILIT CSF.KGUP.VERB.AUTHORITY.CHECK UACC(NONE) 
SETR RACLIST(XFACILIT) REFRESH

RDEFINE CSFSERV CSFKGUP UACC(NONE) WARNING
PERMIT CSFKGUP CLASS(CSFSERV) id(IBMUSER) ACCESS(READ)
SETR RACLIST(CSFSERV ) REFRESH
/*

With the above security setup anyone with read access to the CSFKGUP in class(CSFSERV) gets access to all of the CSFKGUP commands (not just read operations).

This is another master switch, to check the keys security. Its presence or absence controls its use. I had security set up for a key, and with warning mode, which allows the request, and gives a warning message on the console.

RDEFINE XFACILIT CSF.KGUP.CSFKEYS.AUTHORITY.CHECK UACC(NONE)
SETR RACLIST(XFACILIT) REFRESH

Now when I try to delete key SECKEY2 I got

ICH408I USER(IBMUSER ) GROUP(SYS1 ) ...
SECKEY2 CL(CSFKEYS )
WARNING: INSUFFICIENT AUTHORITY - TEMPORARY ACCESS ALLOWED
FROM SEC* (G)
ACCESS INTENT(CONTROL) ACCESS ALLOWED(NONE )

and when I defined a key SECKEY2

ICH408I USER(COLIN   ) GROUP(SYS1    ) NAME(###################
SECKEY2 CL(CSFKEYS )
WARNING: INSUFFICIENT AUTHORITY - TEMPORARY ACCESS ALLOWED
ACCESS INTENT(CONTROL) ACCESS ALLOWED(NONE )

If there is no matching CSFKEYS profile, the userid will be allowed access. When I defined a key ZZZKEY, it worked because there was no matching security profile.

You should define a minimum of

RDEFINE CSFKEYS   C*      AUDIT(ALL) 
PERMIT * CLASS(CSFKEYS) ID(COLIN ) ACCESS(READ)
SETR RACLIST(CSFKEYS ) REFRESH

ISPF ICSF access

You can control which userids can manage ICSF using the ISPF interface. Behind the ISPF interface is code which issues ICSF APIs. You can protect the API calls.

You need to define a resource like

RDEFINE CSFSERV CSFKDSL .... 

and give the userid read access to it.

FunctionThe resources needed
List the contents of a PKDS or CKDSCSFKDSL – list
CSFKDMR – read meta data
CSFKRR2 – read record
Define an AES KeyCSFKRR2 – read record
CSFKGN – key generate
CSFKGN2 – key generate 2
CSFKRC2 -record create 2
CSFKRD – delete record
List the contents of a CKDS elementCSFBRCK
List the contents of a PKDS elementCSFBRPK
Delete a CKDS recordCSFBRCK – control access

The functions and the SAF resource names are defined here.

To define a symmetric cipher key, I needed CONTROL access to the CLASS(CSFKEYS) profile for the key.

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.

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.