Easy AT-TLS configuration and reporting

This blog post discusses AT-TLS configuration, and my EasyAT-TLS code on GitHub to make it easy to configure AT-TLS and give an compact and useful configuration report.

A lot of documentation is written by experts for experts, from the developer’s viewpoint; rather than by experts for people who just want to get the job done. It feels that AT-TLS exposes to much of the structure of how data is held internally. I found it was very easy to get lost, and configuring it is hard. z/OSMF has a “configure AT-TLS” workflow – but it just creates a workflow of the steps. It did not make it easier to configure AT-TLS.

As a general philosophy, rather than the traditional approach of “here are all the keywords in alphabetical order”, I would provide information in different topics.

  • Keywords that everyone uses, and what you will need to get started,
  • More advanced keywords that most people might use,
  • Even more advanced keywords that only experts would use.

and then list the keywords alphabetically within topic. For an inexperienced user this makes it very clear what options they need to specify to get started. If you have a configuration tool (or online documentation) have a button which allows you to specify what your experience level is, and display or configure the information at the appropriate level.

The problem

What does an AT-TLS configuration file look like?

Part of the definition for one rule is

TTLSRule                      COLATTLJ 
{
LocalPortRange 4000
Jobname COLCOMPI
Userid COLIN
Direction BOTH
RemoteAddr 10.1.2.2/32
TTLSGroupActionRef TNGA
TTLSEnvironmentActionRef TNEA
TTLSConnectionActionRef TNCA
}
TTLSGroupAction TNGA
{
TTLSEnabled ON
}
TTLSEnvironmentAction TNEA
{
HandshakeRole ServerWithClientAuth
TTLSKeyringParms
{
Keyring start1/TN3270
}
TTLSSignatureParmsRef TNESigParms
}
TTLSSignatureParms TNESigParms
{
CLientECurves Any
SignaturePairs 060305030403

}......

There is a lot of structure, with values beginning with TTLS.

Without the structure the data looks like

policyRule : COLATTLJ
LocalAddr : All
RemoteAddr : '10.1.1.2/32'
LocalPortRange : 4000-4000
JobName : COLCOMPI
UserId : COLIN
Direction : Both
TTLSEnabled : On
Trace : 255
HandshakeRole : ServerWithClientAuth
Keyring : start1/TN3270
TLSv1.1 : Off
TLSv1.2 : On
TLSv1.3 : Off
HandshakeTimeout : 3
ClientECurves : Any
ServerCertificateLabel : NISTECCTEST
V3CipherSuites : [
003D TLS_RSA_WITH_AES_256_CBC_SHA256,
C02C TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
]

Which I find much clearer.

Once you have configured AT-TLS, you can use the Unix command pasearch to report the configuration. This gives output like

policyRule:             AZFClientRule                                  
Rule Type: TTLS
...
Time Periods:
Day of Month Mask:
First to Last: 1111111111111111111111111111111
Last to First: 1111111111111111111111111111111
Month of Yr Mask: 111111111111
Day of Week Mask: 1111111 (Sunday - Saturday)
Start Date Time: None
End Date Time: None
...
TTLS Condition Summary: NegativeIndicator: Off
Local Address:
FromAddr: All
ToAddr: All
Remote Address:
FromAddr: 0.0.26.137
ToAddr: 0.0.26.137
LocalPortFrom: 0 LocalPortTo: 0
RemotePortFrom: 0 RemotePortTo: 0
JobName: AZF* UserId:
ServiceDirection: Outbound
...

The output for one rule is over 180 lines long, and gives the configuration, including any defaults; not what is actually used. (Some fields can be configured in more than one place, so you need to know which is actually used). Compare this with my tool’s output above.

Easy AT-TLS on GitHub

Displaying a concise view of the data.

On GitHub is code which removes all of the “structure” from the pasearch report to leave just the non default data. You get, for one rule

policyRule : COLATTLJ
LocalAddr : All
RemoteAddr : '10.1.1.2/32'
LocalPortRange : 4000-4000
JobName : COLCOMPI
UserId : COLIN
Direction : Both
TTLSEnabled : On
Trace : 255
HandshakeRole : ServerWithClientAuth
Keyring : start1/TN3270
TLSv1.1 : Off
TLSv1.2 : On
TLSv1.3 : Off
HandshakeTimeout : 3
ClientECurves : Any
ServerCertificateLabel : NISTECCTEST
V3CipherSuites : [
003D TLS_RSA_WITH_AES_256_CBC_SHA256,
C02C TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
]

formatted as a YAML file, which I find much easier to use!

The output of the pasearch file was 2296 lines, the output file from my Python program was 184 lines!

Generating AT-TLS definitions

I found it hard to generate AT-TLS definitions because you need to know the AT-TLS structure; or have an existing entry to copy. For example a keyring is defined in a TTLSKeyringParms definition in TTLSEnvironmentAction statement which is pointed to by a high level TTLSRule. See the partial example above. You should not need to know this information.

As an end user I just want to define a keyring, and let the computer put it into the “internal format”.

With AT-TLS, you can group common information into a unit, and refer to that. I found I got lost trying to work out what I was using, and what else used it. Making one small change is complex as you had to do lots of copying to generate new groups, and then you have these groups lying around which may not be used again.

Generating AT-TLS definitions the easy way

The code to extract the key information from a pasearch output produces a YAML file. The genATTLS code takes a YAML file and generates the AT-TLS input file with all of the structure that AT-TLS requires. The output file can then be used by the Policy Agent on z/OS.

I’ve written the code from an end user perspective. For example I have a YAML file which defines two rules.

---
policyRule : Rule1
Priority : 255
LocalAddr : All
RemoteAddr : All
LocalPortRange : 6794-6794
Direction : Inbound
TTLSEnabled : On
Trace : 255
HandshakeRole : ServerWithClientAuth
Keyring : start1/TN3270
TLSv1.1 : Off
TLSv1.2 : On
TLSv1.3 : Off
HandshakeTimeout : 120
ServerCertificateLabel : RSA2048
---
policyRule : Rule2
Priority : 255
LocalAddr : All
RemoteAddr : All
LocalPortRange : 6793-6793
Direction : Inbound
TTLSEnabled : On
Trace : 255
HandshakeRole : Server
Keyring : start1/TN3270
TLSv1.1 : Off
TLSv1.2 : On
TLSv1.3 : Off
CertificateLabel : RSA2048
ServerCertificateLabel : RSA2048

This can be simplified by using “BasedOn” to refer to other sections, such as the “common” rule

---
# This first section is common to the others
policyRule : common
TLSv1.1 : Off
TLSv1.2 : On
TLSv1.3 : Off
Keyring : start1/TN3270
Priority : 255
LocalAddr : All
RemoteAddr : All
Direction : Inbound
TTLSEnabled : On
Trace : 255
CertificateLabel : RSA2048
ServerCertificateLabel : RSA2048
---
policyRule : Rule1
BasedOn : common
LocalPortRange : 6794-6794
HandshakeRole : ServerWithClientAuth
HandshakeTimeout : 120
---
policyRule : Rule2
BasedOn : common
LocalPortRange : 6793-6793
HandshakeRole : Server
---

The Python script generates the definitions from this file, 110 lines of output from a 28 line input file!

It is easy to extend. You just specify the overrides

policyRule : Rule2A
BasedOn : Rule2
TLSv1.3 : On
Priority: 300

The priority:300 says this definition should be used over Rule2, because Rule2 only has priority 255.

Testing multi system ICSF on a single system.

I only have a single z/OS image, and as part of testing ICSF in a multi system environment, I needed a second ICSF environment. I am the only user on my system, so I can stop and start ICSF as I please.

I set up a second set of ICSF datasets, and a second CSPRMxx parmlib member. I then used

P CSF
S CSF,PRM=C2
D ICSF,KDS

The D ICSF,KDS command, tells you what ICSF data sets are being used.

When using CSFKGUP I was careful to specify the correct dataset

//STEP10 EXEC PGM=CSFKGUP
// SET CKDS=COLIN.SCSFCKDS.NEW

To go back to my primary system.

P CSF
S CSF

What ICSF APIs do I need to use for AES CIPHER keys

I’ve done a lot of work using ICSF to generate keys programmatically for data set encryption, and I found it hard to remember what the different APIs are,and what options to specify. This blog post covers the main APIs.

AES keys are better than DES keys, because they are more secure (take longer to break) and use less CPU.

There are different key tokens formats within ICSF. AES uses a variable key format. Within AES keys you can have types

  • CIPHER used for encrypting data – such as data set encryption
  • EXPORTER and IMPORTER, these are used to encrypt keys for sending to remote systems. They are known as Key Encrypting Keys.
  • DATA – CIPHER keys are better than DATA because they have more granular control. You can say a CIPHER key can be used to encrypt an AES key – but not a DES key. With DATA it is all or nothing.
  • Others – I haven’t played with these

For many APIs you can give the name of a token in the CKDS data set. The names are 64 characters long. You can also use a copy of a token in memory, for example your program reads a token from the CKDS, or you have been sent a token from a remote systems. These will have a length typically shorter than 64 characters. ICSF uses the length you specified to determine what information is being passed in.

Generating a key token

You can use CSNBKGN2 (Key Generate2) to generate a key token.

You can specify type CIPHER. This generates a key token using some defaults. The defaults are not suitable for generating keys for data set encryption.

You can specify type TOKEN. You pass a skeleton token key with some of the options specified. You could generate this yourself, setting all of the bits you need, but it is easier to use the Key Token Build2 (CSNBKTB2) API to generate the key token. For example you specify rule options as character strings such as “CIPHER”,”XPRTCPAC”,”ANY-MODE”, which are needed for data set encryption.

You can specify the hex string to be used as the key value, by using the skeleton. You specify clear_key_bit_length and the clear_key_value. If clear_key_bit_length is zero in the skeleton, no key value is used and the system generates a random value. You will need to specify a valid value in the Key Generate2 field clear_key_bit_length. If you want to specify the hex value, set the two clear_key_bit_length fields to the same value in CSNBKTB2 and CSNBKGN2.

You can create a key token for use

  • on the current system. Once you have successfully generated your key token, you can use it to encrypt data, but you are more likely to store it in the local PKDS service using the PKDS Key Record Create (CSNDKRC) API. You might need to use the Key Record Delete (CSNDKRD) API if the key already exits in the PKDS. There is no write with replace service.
    • At a later date you can use the ICSF API to extract the key and encrypt with an EXPORTER or IMPORTER key to send to a remote site
  • and/or you can get a copy encrypted with a Key Exporting Key (EXPORTER or IMPORTER) so it can be securely sent to another site. You can
    • export it using an EXPORTER Key Encrypting Key to send to a remote site.
    • export it using an IMPORTER key, so it can be imported into the local system at a later date. I do not think this is a common scenario.

List the CKDS

You can search the CKDS passing various criteria using function Key Data Set List (CSFKDSL). It returns the label of each record matching the criteria. For each record you can use Key Data Set Metadata Read (CSFKDMR) which displays information such as create time, and if it is archived or not. You can read the record from the CKDS using CKDS Key Record Read2 (CSNBKRR2). The actual key value is encrypted, but there are many fields which are available, such as this is an AES CIPHER key.

You can get the size of the key value within the token using the Key Test2 (CSNBKYT2) API, with the KEY-LEN option.

Get the checksum of the token

There is an API to get the checksum of a token. (I do not think this checksum is stored in the token, as you can specify different algorithms.) If you send the token to a remote system, you can check the checksum is the same as the original, and that the token has not been changed. The Key Test2 (CSNBKYT2) API, with the GENERATE option, returns the checksum value. You can specify which algorithm to use, for example SHA-256 or ENC-ZERO. There is a VERIFY option which seems to compare the value you pass with the calculated value, and if they do not match gives reason code 9000.

Reading a key from the CKDS

You can use the CKDS Key Record Read2 (CSNBKRR2) API to read a record from the CKDS into memory. There is an API CKDS Key Record Read (CSNBKRC2) which reads old format records from the CKDS.

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.

Setting up ICSF security for access to keys.

The documentation for ICSF and securing access to keys is hard to follow. There is a lot of it, but is not easily consumed. The security model has evolved over time, and I found the definitions confusing.

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 to protect keys.

“Required” definitions

You should define the profile below to give you granular controls.

RDEFINE XFACILIT CSF.CKDS.TOKEN.CHECK.LABEL.FAIL 
SETROPTS RACLIST(XFACILIT) REFRESH

If the profile CSF.CKDS.TOKEN.CHECK.LABEL.FAIL is defined, a userid needs the appropriate access to the CSFKEYS profile:

  • READ authority to read a label from the CKDS,
  • UPDATE authority if creating a label,
  • CONTROL authority if writing to or deleting a label.

A userid will also need access to the ICSF commands or APIs to be able to use the key.

If the CSF.CKDS.TOKEN.CHECK.LABEL.FAIL profile is not defined, and the userid has at least read access to a profile for the key, the userid can create, write or delete a label. If there is no profile for the key the userid has access to the key.

If you define the profile RDEFINE XFACILIT CSF.CKDS.TOKEN.CHECK.LABEL.WARN instead of CSF.CKDS.TOKEN.CHECK.LABEL.FAIL, and the userid has at least read access to a profile, the userid can create, write or delete a label. In addition it produces a warning message.

This feels like the same as RDEFINE XFACILIT CSF.CKDS.TOKEN.CHECK.LABEL.FAIL WARNING, so I do not see why RDEFINE XFACILIT CSF.CKDS.TOKEN.CHECK.LABEL.WARN is needed.

“Required” access control for exporting keys.

If you want to control who can export AES and DES keys you can enable this by defining a profile

RDEFINE XFACILIT CSF.XCSFKEY.ENABLE.AES
RDEFINE XFACILIT CSF.XCSFKEY.ENABLE.DES
SETROPTS RACLIST(XFACILIT) REFRESH

This turns on the checking system wide, you do not need to give any userid access to it.

To protect the export of all keys,

RDEFINE XCSFKEY * UACC(NONE)
PERMIT * CLASS(XCSFKEY) ID(SECADMN) ACCESS(UPDATE)
SETROPTS RACLIST(XFCSFKEY) REFRESH

You can generate individual or generic profiles for specific keys.

This security model feels broken, as I can read a key from the CKDS (encrypted with the hardware keys) write it to a file. Send the file to a remote system, and if they remote system has the same hardware keys, write the key to the CKDS. I still need access to read and write the key – but I do not use the XCSFKEY profile.

Defining profiles

The easy bits

You can define a standard security resource, such as

RDEFINE CSFKEY COLINSKEY ...
PERMIT  COLINSKEY CLASS(CSFKEY) ID(...) ACCESS(...)

Where COLINSKEY is the label of an entry in the CKDS or the PKDS. This can be a CIPHER key or a KEK key.

If there is no matching profile, there are no restrictions, so you may want to define a default

RDEFINE CSFKEY * UACC(NONE) AUDIT(ALL) warning

Where AUDIT(ALL) says report every time it is used, and WARNING reports if there is a security violation and gives access anyway. You can then use the information to create more specific profile. Once you have configured security you can remove the warning option, and protect your resources.

You need to specify access:

  • READ to be able to read the profile
  • UPDATE authority if creating a label,
  • CONTROL authority if writing to or deleting a label.

But see “Required” definitions and the CSF.CKDS.TOKEN.CHECK.LABEL.FAIL to enable the granular access. Without the CSF.CKDS.TOKEN.CHECK.LABEL.FAIL profile, if a userid has at least read access to the CSFKEY profile, it can do anything with it. If there is no profile for the key, any userid can do anything with the key.

Which profile is used?

There are different ways of using a key (especially with APIs)

  1. Use the label character string of a key in the CKDS. You can pass a label into an API request. The label name is used in the security checks.
  2. You can read the token from the CKDS using the label, and pass the token (a long string of internal format data) to an API request. ICSF compares the token with the CKDS and if it locates a record with the same key value – it uses the label of the record in the security checks.
  3. You can get the token from elsewhere, for example read a data set. The same checks are done as in 2. above.
  4. If a record with the same key value was not found in the CKDS, (so you are using a key which your CKDS does not know about)
    • If the profile CSF.CKDS.TOKEN.CHECK.LABEL.WARN or CSF.CKDS.TOKEN.CHECK.LABEL.FAIL in class(XFACILIT) is defined, and profile CSF.CKDS.TOKEN.CHECK.DEFAULT.LABEL in class(XFACILIT) is defined, then check the userid’s permission to profile CSF-CKDS-DEFAULT in class(CSFKEYS). See here.

What access do I need to the profile?

If you have CONTROL access to CSFBRCK CLASS(CSFSERV), and CONTROL access to the key’s profile SECKEY2 CL(CSFKEYS) you can delete the key SECKEY2.

If you have ALTER (more powerful than CONTROL) access to CSFBRCK CLASS(CSFSERV), the CSFKEYS profile is ignored you can delete any key.

You might want to give administrator only CONTROL access to the commands and APIs.

What are the ICSF bits?

I struggled for a while to understand the different parts of ICSF.

Definition of terms

You use a symmetric key for encrypting and decrypting data. You use the same key for encryption and decryption.

An asymmetric key has two parts. You encrypt with one, and decrypt with the other. If you encrypt with a key, you cannot use your key to decrypt it. You need the other part. You pick one part, and keep it secure – this is called the private key. The other part you can make this available to every one – the public key.

A certificate is a collecting of information. It contains a public key, and information about the owner of the certificate. I think of a certificate as a paper envelope in which you put the public key, they owner information, and other information, such as validity dates.

Hardware encryption. These days the calculations for encryption can be offloaded to special cryptographic hardware.

Cryptographic keys can be stored in hardware. The hardware can have a cryptographic key, which can be used to encrypt data before storing it in the hardware.

You can request the hardware to extract the data, and encrypt it with another key. This way the encryption key is never seen outside of the hardware.

ICSF provides an API to use z/OS hardware cryptographic functions, and some facilities to mange the keys.

ICSF

A symmetric key can have different algorithms. Typically RSA and AES. RSA came first, but AES is considered better as it takes more work to crack, and uses less resources when encrypting and decrypting data.

With ICSF, the symmetric keys are stored (encrypted) within the ICSF CKDS database. The keys are referenced to using tokens. You can have:

  • A null token – used as place holder.
  • A fixed length token, this is an old format token.
  • A variable length token. As cryptography developed over time, all the information could not be stored in a fixed length, and so variable length tokens were introduced.

With ICSF, Asymmetric keys are called PKA (Public Key algorithms).

An asymmetric key can have different algorithms. Typically RSA, Elliptic Curve (EC), and various Crystal methods (these are based on complex calculations based on crystal lattices).

The asymmetric keys are stored (encrypted) within the ICSF PKDS databases. The keys are referenced to using tokens.

As with symmetric keys, the early asymmetric tokens were a fixed length. As cryptography developed, variable length tokens were introduced.

Symmetric ciphers in more depth

ICSF has two forms of symmetric cipher for encrypting user data

  • DATA – I think of this as version 1. The key value can be extracted as cleartext – this is not very secure.
  • CIPHER – I think this as version 2. The key value is always encrypted – either within ICSF, encrypted on the hardware, or with a transmission key.

Best practice is to use CIPHER instead of DATA. There is an older API function for generating the DATA keys, and a newer API function (CSNBKTB2) for the CIPHER keys. The newer function has more capability. For example, you can specify options that allow/prohibit exporting the key, and which encryption modes can be used, such as cipher block chaining(CBC).

  • AES has one type of cipher: CIPHER. This can be used for data set encryption and in encrypt API (CSNBENC), and the decrypt API (CSNBDEC).
  • DES has three types of cipher
    • DATA which can be used to encrypt and decrypt. This can be used for data set encryption and in encrypt API (CSNBENC), and the decrypt API (CSNBDEC).
    • ENCIPHER. This can be used in the encrypt data API (CSNBENC).
    • DECIPHER. This can be used in the decrypt data API (CSNBDEC).

  • To use the ENCIPHER function you need a DATA, CIPHER or ENCIPHER key
  • To use the DECIPHER function you need a DATA, CIPHER or DECIPHER key
  • To use data set encryption you need DATA or CIPHER (because you need to do encryption and decryption).

Generating symmetric keys

You can use the ISPF panel interface.

For example, to create a symmetric key; from the ICSF main page select options:

  • 5 UTILITY
  • 5 CKDS KEYS – Manage keys in the CKDS
  • 8 Generate AES CIPHER keys

You specify the name of the key, the AES key bit length, which encryption mode and if it can be exported from the hardware (XPRTCPAC).

The ISPF panels provide defaults. You can see the values by using the ISPF interface to display entries in the CKDS. For example

Key usage:ENCRYPT and DECRYPT, and ANY-MODE

Key management: gives XPRTCPAC and other keys. For example XPRT-AES says the key can be exported and encrypted using an AES Key Exporting Key.

Use the CSFKGUP batch interface.

The ICSF administrators guide gives details of the utility. It uses data sets (which may not be the active ones). You need to tell ICSF to refresh the in-memory information after the data sets have been updated.

For example

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

The statements in CSFIN are like

DELETE TYPE(CIPHER) LABEL(AESCIPHER) 
ADD TYPE(CIPHER ) ALGORITHM(AES) LENGTH(32),
LAB(AESCIPHER)

You can specify values for KEYUSAGE and KEYMGMT for example

KEYUSAGE(ANY-MODE) KEYMGMT(XPRTCPAC).

CSFKGUP seems to provide the same capability as the ISPF interface.

If you use CSFKGUP you need to issue a program to refresh the in memory information from the updated CKDS and TKDS data sets.

How is ICSF configured?

ICSF is started as a started task, so uses JCL in the SYS1.PROCLIB concatenation. I have member CSF in USER.Z31A.PROCLIB.

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

The parameters are selected when the procedure is started. For example

S CSF,PRM=C2

The parameters come from member CSFPRMxx in the parmlib concatenation. My default parameters are in USER.Z31A.PARMLIB(CSFPRMCP)

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

To use different data sets, you need to stop ICSF, and restart it with a different configuration member, specifying different data set names.

Using ICSF within a single image or sysplex

This blog post describes using ICSF within one system. This could be a sysplex if all systems use the same ICSF data sets. It gives some guidance on setting up your environment, and some implications of your decisions.

For some guidance on defining keys for Data Set Encryption see here(how many keys?). For a deep dive on key management which talks about changing the keys on the ICSF data sets (key rotation) see here.

Introduction

This section gives some general information about using ICSF, keys and data set encryption.

  • You set up a symmetric key for data set encryption.
  • You can specify which key a data set should use, when the data set is created.
    • You can configure a RACF profile to specify the key name in the DFP information.
      • When you allocate a data set which uses this RACF profile, the key is extracted from the DFP information and used.
    • The data set must be SMS-managed extended format. (You can configure this either in JCL, or with SMS options)
    • You can specify the keylabel in the JCL
    • You can specify in the SMS Data class. With SMS you configure ACS with statements like if the data set attributes are … then use data class xxxx, which in turn has a data set encryption key specified.
  • The key name is stored with in the catalog along with other SMS information.
  • If you change the label in the RACF profile, any new dataset which get created get the updated label, existing data sets are unchanged.
  • If you delete and recreate a key for a label, any data set which used the old label becomes unreadable.
  • You can unload and reload a key using my programs in EASYICSF on Github. ICSF does not provide this capability. When you define a key, you might unload it, and archive it away. Then, if the key gets deleted by accident, you can then recover by reloading it.
  • Keys stored by ICSF are encrypted before they are stored in the ICSF data set. The master encryption key for these data sets can be stored in the cryptographic hardware. If someone steals the ICSF data sets, they cannot use them, unless they have the master key as well.
  • You can get ICSF to use a new master password. It will take each entry in the ICSF data set, decrypt it with the old key, encrypt it with the new key, and store it back in (a new) data set. This is known as key rotation. (This is a strange use of the term rotation because there is no rotation, you just change the key.)
  • If you want to use a new key for a data set. You have to copy the contents of the data set to a new data set set up with the new key. You then rename the data set. You cannot just change the key.
  • You can specify a start and end date for a key. The start date can be today’s or any date in the future. The end date is after the value (this means you cannot specify an end date, so it can not be used today.)
  • If you want to stop a key from being used, you can set an archive flag on it. (If you delete it, any data sets using this key cannot be processed). By default the application using an archived key will fail. You can configure ICSF so that use of an archived key will work, and you get a message for the key on the ICSF joblog, and a record in SMF.
  • You can control who can use keys by a RACF profile in class CSFKEYS. If you use CSF.CSFKEYS.CONDITIONAL.ACCESS.CONTROL you can restrict what functions userids can execute – and so prevent most people from being able delete keys.
  • Copying data sets
    • If you use DFDSS backup, the encrypted data is backed up. When you restore it, it allocates a data set with the same encryption label. The intermediate file is not encrypted. You can still read the intermediate file and see the meta data – but the original file contents are still encrypted.
    • If you use IEBGENER to copy the data, if the output data set does not have an encryption label, the output data set is encrypted with the same key label as the input.
    • To specify a key label you can specify DSKEYLBL in JCL and in the TSO ALLOC command.
    • In an application
      • you can specify the key label using SVC 99 (dynamic allocation).
      • you cannot specify it using the C function fopen(). You will need to specify it in JCL and use DD:name to refer to the data set.
    • If you use other program, such as a ISPF edit or a C program, to read the file and write the contents out, by default the output file is not encrypted. The application sees the clear text, and the output is in clear text unless you have specified a key label for the output data set.

Planning

You need to consider how many keys you need. For example, one for all your data sets, or one per business function such as online banking, or stocks and shares.

Keys for some files, such as a database will be long lived – as long as the data base exists, or until you copy it with a new key. The data set may have been archived to tape, and could be recalled years later.

You need to plan who has access to the data set. For a DB2 data base data set, people will not normally need access to it, as the access is managed by DB2. The DB2 system userid will need access. Storage managers, responsible for backing up the data sets do not need access to the key, and access the encrypted data without decryption.

If someone has access to an encrypted data set, you need to ensure that any data set they create which may have sensitive information in it is also encrypted. This may mean reviewing userids and what they are used for. For example if you have a data set ENC.DATASET1 which userid COLIN can access, do you need to ensure that any data set that userid COLIN can create is encrypted?. This may mean data sets are encrypted which do not contain sensitive information. If userid COLIN can also create data sets beginning with SYS2 – you need to consider if all SYS2.* datasets need to be encrypted.

You might define a userid COLINE which can only access the encrypted data sets, and can only created data sets prefixed with COLINE – and these are all encrypted. Userid COLINE does not have access to general data sets, such as SYS1.PARMLIB.

For short lived data sets you could change the key every few months.

You need to consider backups and disaster recovery, and how your remote sites will use the datasets. The remote sites will need the same keys for the data sets, as the primary site.

Changing keys

As a general rule, you do not want to delete a key unless you are sure that is is not needed (including any data sets that may have been archived off the system). If you want to generate new keys periodically you may want to use a naming convention such as PAYROLL2024NOV, then generate a new key PAYROLL2025NOV, and update your definitions (or SMS).

Using keys

You can specify the encryption key to be used in

  1. The RACF profile used for the data set. The value is in the DFP segment.
  2. The JCL DSKEYLBL parameter on the DD statement.
  3. In the SMS data class used for the data set. This can be specified in the DATACLAS parameter on the DD statement, or selected via the ACS(Automatic Class Selection) routine.

The key is used when the data set is allocated(created).

If you change the RACF profile, the updated label will be used next time a data set is created.

If people specify the key either by application on JCL (the DSKEYLBL option on the DD statement), changing the key name will mean changing the applications and JCL to use the new key.

An ACS routine has logic like

if the data set names begins with … , and has properties …. then use the following volumes…, make it this size…, and use the Data Class … which has the Data Set Key Label…)

If you change the Data Set Key Label in the Data class, the next time the Data Class is used, it will use the updated key. This is much simpler than trying to change JCL.

It looks like it is easier to use the RACF profile or SMS if you will be changing the data set encryption key.

Creating keys

  • You can use the ICSF utility CSFKGUP to generate keys. The syntax is like ADD TYPE(CIPHER) LABEL(COLINKEY)…
  • You can use the ISPF interface to define keys. From the main panel, options 5 UTILITY -> 5 CKDS KEYS, option 7 or 8 to define a symmetric key.
  • You can use ISPF to generate statements for CSFKGUP. From the main panel option 8 KGUP – Key Generator Utility processes. This simplifies selecting the options. For example if you select the action to maintain (add) an entry, if you specify algorithm DES, it will display the valid key types for DES (eg CIPHER, DATA, ENCIPHER…). If you select algorithm AES, it will display the valid key types for AES ( eg CIPHER, DATA, EXPORTER…). If you use this method, you may still have to edit the statements manually, because not all the required parameters are specified, see below.

For some keys you need to have a RACF profile defined for the label name. For example

RDEFINE CSFKEYS DES5 UACC(NONE) –
ICSF(SYMCPACFWRAP(YES) SYMCPACFRET(YES))

This seems to be a strange way of storing this information.

Setting up data set encryption

Best practices.

Use definition like

ADD ALGORITHM(AES) TYPE(CIPHER) KEYUSAGE(ANY-MODE) LENGTH(32), 
KEYMGT(XPRTCPAC)LABEL(COLINCIPHER2 )

Where

  • ALGORITHM(AES) is better than ALGORITHM(DES). It is harder to crack, and uses less CPU.
  • TYPE(CIPHER) is better than TYPE(DATA).
  • KEYUSAGE(ANY-MODE) is needed for data set encryption with AES.
  • LENGTH(32) is the longest and strongest AES key size.
  • KEYMGT(XPRTCPAC) is needed for data set encryption.
  • LABEL(COLINCIPHER2 ) you specify your label.

If you use the ISPF panels to generate the key, you can specify all of the parameters. If you use the ISPF interface and 8 KGUP – Key Generator Utility processes. This does not specify create KEYUSAGE and KEYMGT values, which will have to be entered manually.

Afterthoughts

You need to backup the ICSF data sets.

If you need to use a (restored) data set with a different name, you will need to restart ICSF with a different parmlib member containing the different names.

ICSF does not provide a way of copying between ICSF data sets, but you can use UNLOAD and RELOAD programs in my EASYICSF on Github which allows you to copy a key from one ICSF data set to an intermediate data set, then reload it into a different,or the same, ICSF data set.

You should have a process, which you have tested, for replacing keys which may be been exposed. This may cause a long (hours) outage, so you need to have JCL and definitions written before you need them.

All I ever wanted to know about ICSF and data set encryption keys

z/OS systems have cryptographic processors, which can be used to offload expensive cryptographic operations. You can configure z/OS so if you generate a cryptographic key, you do not see the actual, clear text key (so it cannot easily be stolen). This is done by the cryptographic processors having a cryptographic key themselves, and any keys they emit, are encrypted with this hardware key. When you use your cryptographic key, it is passed to the cryptographic processors, which then decrypts the data using the hardware key, then uses your real cryptographic key.

ICSF provides an interface to do cryptography, use the cryptography processors and to mange cryptographic keys. Most z/OS user will ICSF capabilities without knowing about it.

ICSF works at the level of symmetric and asymmetric keys. You cannot use it to generate digital certificate, do TLS handshakes, nor do data set encryption (but data set encryption uses ICSF keys).

ICSF provides

  • An ISPF interface to mange keys. You can list, define, delete etc keys through the panels
  • A batch program CSFKGUP which can be used to so some of the key management. This acts on a data set. You need to tell ICSF to refresh the in-memory contents from the data set.
  • An extensive set of API’s if you want to write your own application, for example to generate a key, or to encrypt some data. It is relatively easy to write Rexx programs to use the APIS. These APIs work on the active ICSF data sets. To use different data sets you need to change the ICSF configuration.

Having spent a few weeks trying to use it I feel “how to” information is missing. I tried implementing data set encryption across disconnected system and found ICSF is missing some functions, for example batch program to export and import keys, and a batch program to securely generate shared keys on different systems.

In this blog post, I’ve tried to fill in the holes in the documentation, and provide the missing batch utility programs.

I’ve written blogs posts on

I’ve put my utilities and sample code on Github.