First steps in using system ssl

System SSL also known as GS kit provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

This blog post covers background information for using GSKit. I wrote it because I could not find useful samples to get me started.

Introduction

It took me some time to understand how System SSL has been written. Now I understand it, I think it is a good API. They can be more than one way of doing something, so working out what you want to to can be hard.

Use of structures

It took me a while to understand the structures. I thought they were a little complex, and started designing my own equivalent, but I then found my “improved” definition were very similar to the original ones.

For example, I want to process some application data. It has an address and a length.
With my simplistic view I would define

  • char * pBuffer;
  • int lBuffer;

You could the use these in your application.

GSKIT provides the same files in a structure.

gsk_buffer mybuffer;
mybuffer.data = "ABCD"; // data is a void * pointing to the data
mybuffer.length = 4; //

I can then do

gsk_buffer in;
gsk_buffer out;
in.data = ...
in.length = ...
gsk_rc gsk_copy_buffer(*in,*out)

For gsk_buffers that are output from a GSKIT call you need to free the contents.

gsk_free_buffer(&in);

This will release any storage which had been allocated to the structure, and set the values to 0.

Do not use this for gsk_buffers you created, because it will free the storage the data is using.

Arrays of objects

Some API functions take one certificate, and others take an array of certificates.

You define one certificate-key

 pkcs_cert_key cert_key;

For an array of certificate keys you use

// Define the array (of 2)
pkcs_cert_key cert_key[2];
// Initialise it
cert_key[0].certificate = cert;
cert_key[0].privateKey = pRecord -> privateKey;
cert_key[1].certificate = cert1;
cert_key[1].privateKey = pRecord1 -> privateKey;
// Define the structure of array of keys
pkcs_cert_keys cert_keys;
cert_keys.count = 2;
cert_keys.certKeys = &cert_key[0];

Processing arrays of objects

void print_certs(pkcs_certificates * pcs)
{
// pkcs_certificate has a count and array of certs
// iterate over them all
int i;
pkcs_certificate * pc = & pcs-> certificates[0];
for (i = 1; i <= pcs-> count;i++)
{
printf("Certificate %i\n",i);
printf("==========\n");
print_cert(pc);
pc ++;
printf(" \n");
}
}

Where print_cert() is the program I wrote which prints information about one certificate.

Useful functions

Most gskit functions return a gsk_status. You can print the interpretation of this using

if ( gskrc != 0)
printf("Error return: %s\n" gsk_strerror(gskrc));

Definitions are in

  • /usr/include/gsktypes
  • /usr/include/gskcms.h

First steps in using system ssl: compiling a program

System SSL also known as GS kit provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

See First steps in using system ssl gives some introductions to gskit.

The blog post provides the script I used to compile my programs. Not everything may be relevant.

name=main
export _C89_CCMODE=1
p5="-I. -I/usr/include "
p7="-Wc,DLL "
p8="-Wc,LIST(c.lst),SOURCE,XREX,SHOWINC"
p8=" " # no listing
/bin/xlc $p5 $p7 $p8 -c $name.c -o $name.o
l1="-Wl,LIST=ALL,MAP,XREF "
bind=" /usr/lib/GSKCMS31.x /usr/lib/GSKSSL.x "
/bin/xlc $name.o ${bind} -o $name -v $l1 1>bind.lst

The output from the C compile goes into c.lst, and from the bind it goes into bind.lst

The gsk includes are in /usr/include.

For the bind you need /usr/lib/GSKCMS31.x /usr/lib/GSKSSL.x

Example of system ssl to display keyring information

System SSL also known as GS kit provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

This blog post covers how to access a keyring and use the information in it. I wrote it because I could not find useful samples to get me started.

Introduction

It took me some time to understand how System SSL has been written. Now I understand it, I think it is a good API. They can be more than one way of doing something, so working out what you want to to can be hard.

List the certificates in a keyring

// #include <gskssl.h> // only used for TLS and SSL
#include <gskcms.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <printhex.h>
int main(int argc, char * argv[]) {
int rc;
gsk_status gskrc;
gsk_handle hRing ;
int count = 0;// the number of certs is put here
char * ring = "START1/TN3270";
printf("Open keyring %s\n",ring);
gskrc= gsk_open_keyring (ring,
&hRing,
&count );
if (gskrc != 0)
{
printf("gsk_open_keyring %s\n", gsk_strerror(gskrc));
return 8;
}
printf("Number of certs in the ring %i\n",count);
//
// Get the default label (if it exists)
//
char * default_label;
gskrc = gsk_get_default_label(hRing,&default_label);
if (gskrc != 0)
{
else printf("gsk_get_default_label error %s\n",gsk_strerror(gskrc));
return 8;
}
printf("gsk_get_default_label %s\n",default_label);
//
// now get the record with this label (or by number)
//
gskdb_record * pRecord = 0;
gskrc = gsk_get_record_by_label(hRing,default_label,&pRecord);
//gskrc = gsk_get_record_by_id(hRing,4,&pRecord); // by number
if (gskrc != 0)
{
printf("gsk_get_record_by_label worked %s\n",gsk_strerror(gskrc));
return 8;
}
// The value is a half work enum
printf("Record Type %hi\n",pRecord ->recordType);
#include <readLabels.h>
#include <listall.>
gskrc = gsk_close_database(&hRing);
return 0 ;
}

ReadLabels

With gsk_get_record_labels you can request those with private keys (1) or those without private keys(0)

If you want all certificates on the ring use without private_keys(0).

gskrc = gsk_get_default_label ( hRing,&default_label);
if (gskrc == 0)
printf("gsk_get_default_label %s\n",default_label);
else printf("gsk_get_default_label error %s\n",gsk_strerror(gskrc));
char ** pl = 0;
int countL = 0;int whichRecords = 0;
printf("/nGet records with no private key (%i)\n",whichRecords);
gskrc = gsk_get_record_labels (hRing,
whichRecords, // those with public keys only
// 1 for this with private key
&countL, // How many returned
&pl); // char *** labels
if ( gskrc != 0)
{
printf("gsk_get_record_labels%s\n",gsk_strerror(gskrc));
return 8
}
for (int j = 0; j<countL ;j++)
{
printf("Label %i,%s\n",j,*pl);
pl++;
}
printf("============\n");

It took me some time to get the list of labels printed out. The value is a char ***;

  • If you had char * p; p is a pointer to a string so is a char *.
  • char * q[10]; pl = &q[0]
  • pl is a pointer to the elements in the array. pl is addr(a char * ) so char **;
  • the address of pl is passed to the function, so it is addr( a char **) so is a char ***
  • In the loop we get what pl points to (*pl). This is a pointer to a string. so we have printf(“…”,*pl);

Listall

printf("Number of records %i\n",count);
for (int j = 1 ; j<count+1 ;j++) // starts at 1
{
gskrc = gsk_get_record_by_index(hRing,j,&pRecord);
printf("Record %i:n",j);
printRecord(pRecord);
}
printf("============\n");

printRecord

void printRecord( gskdb_record * pRecord)
{
printf("Record Type %hi\n",pRecord ->recordType);
printf("Record Flags %2.2x\n",pRecord ->recordFlags);
printf("Record ID %i\n",pRecord ->recordId);
printf("Record Issuer ID %i\n",pRecord ->issuerRecordId );
printf("Record Label %s\n",pRecord ->recordLabel);
printf("============\n");
}

Output

The program printed out

Open keyring START1/TN3270
Number of certs in the ring 9
gsk_get_default_label NISTEC256
gsk_get_record_by_label worked
precrd 2 20a2e6e0
Sizeof gskdb_record_type 2
gsk_get_default_label NISTEC256

Get records with no private key (0)

Label 0,DOCZOSCA
Label 1,JUN24CA
Label 2,DocZosCADSA
Label 3,CA256

============
Number of records 9
Record 1:
Record Type 2
Record Flags 80
Record ID 1
Record Issuer ID 1
Record Label DOCZOSCA
...

Record 9:
Record Type 3
Record Flags c0
Record ID 9
Record Issuer ID 1
Record Label NISTEC256
============

  • The value of Record 1 type gskdb_rectype_certificate = 2, /* Certificate */
  • Flags is
    • GSKDB_RECFLAG_TRUSTED = The certificate is trusted ( 0x80);
    • GSKDB_RECFLAG_DEFAULT = This is the default key
  • ID 1 – record 1
  • Issuer ID 1 – it is self signed
  • Label DOCZOSCA
Record 9:
Record Type 3
Record Flags c0
Record ID 9
Record Issuer ID 1
Record Label NISTEC256

  • The record type is 3 gskdb_rectype_certKey = 3, /* Certificate with key */
  • Flags is
    • GSKDB_RECFLAG_TRUSTED = The certificate is trusted.
    • GSKDB_RECFLAG_DEFAULT = This is the default key
  • ID 9 – it is record 9 in the keyring
  • Issuer ID 1- it was signed by the certificate with position 1 (DOCZOSCA)
  • Its label is NISTEC256

Example of using system ssl to print certificate information

The structure containing information about a certificate has a very deep nesting of structures and arrays. Fortunately for the most common information, gskit provides an interface.

There is a standards document giving the layout of the certificate, and what the fields mean.

The structures are in /usr/include/gskcms.h. Some of the data types are in /usr/include/gsktypes.h .

The certificate structure

  • The structure pkcs_certificate>s has an array of pkcs_certificate and a count of the elements.
  • The structure pkcs_certificate has a certificate type and an x509_certificate.
  • The structure x509_certificate has
    • a tbsCertificate
    • signature algorithm
  • The x509_tbs_certificate structure (tbs from To Be Signed) has
    • the certificate version
    • the certificate serial number
    • the signature algorithm of the certificate
    • the issuer
    • the validity period
    • the subject.

The fields

  • The certificate serial number. This a hexadecimal value. The definition has a pointer to the data, and a length of the data. The length is at least one.
  • The signature algorithm of the certificate. This includes
    • The signature type. This is a C enum, such as x509_alg_sha256WithRsaEncryption = 25
  • The issuer is a complex nest of structure. Fortunately gskit provide a function gsk_name_to_dn(structure,name). The output “name” is a printable EBCDIC string like CN=DocZosCA,OU=CA,O=COLIN
  • The validity period. This structure contains two time-value fields, not before, and not after.
    • You can format a time value field using
      • char dest[18];
      • strftime(dest,18,”%y/%m/%d %H:%M:%S”,timeval);
  • The subject is like the issuer. Use the function gsk_name_to_dn to convert it to printable EBCDIC. For example CN=10.1.1.2,OU=SSS,O=NISTECC521

Printing the structures

I have some functions like

void print_certs(pkcs_certificates * pcs)
{
// pkcs_certificate has a count and array of certs
// iterate over them all
int i;
pkcs_certificate * pc = & pcs-> certificates[0];
for (i = 1; i <= pcs-> count;i++)
{
printf("Certificate %i\n",i);
printf("==========\n");
print_cert(pc);
pc ++;
printf(" \n");
}
}

which invokes

void print_cert(pkcs_certificate * pc )
{
print_x509_algorithm_type(pc ->u.certificate. signatureAlgorithm
.type,0);
// Serial number
char output[60]; // into here
// hexof is written by Colin Paice
printf("Serial number Hex :%s\n",hexof(
pc ->u.certificate.tbsCertificate.serialNumber.data,
pc ->u.certificate.tbsCertificate.serialNumber.length,
&output[0]) );
// get the subject and issuer names
gsk_status gskrc;
char * pName; // returned here - must free it
pName = "Unknown";
gskrc = gsk_name_to_dn(
& pc ->u.certificate.tbsCertificate.subject,
&pName);
if (gskrc != 0)
printf("Subject gsk_name_to_dn %s\n", gsk_strerror(gskrc));
printf("Subject name :%s\n",pName);
gsk_free_string(pName);
// extract the issuer,
pName = "Unknown";
gskrc = gsk_name_to_dn (
& pc ->u.certificate.tbsCertificate.issuer,
&pName);
if (gskrc != 0)
printf("Issuer gsk_name_to_dn %s\n, gsk_strerror(gskrc));
printf("Issuers name :%s\n",pName);
gsk_free_string(pName);
// extract the valid times.
char buffer[18];
printf("Not Before :%s\n",
ccpformattime (
& pc ->u.certificate.tbsCertificate.validity.notBefore,
&buffer[0] ) );
printf("Not After :%s\n",
ccpformattime (
& pc ->u.certificate.tbsCertificate.validity.notAfter,
&buffer[0] ));
print_x509_extensions(&pc ->
u.certificate.tbsCertificate.extensions);
}

Process DER ASN.1

Some data is encoded in ASN.1 format.

This is of format

  • 1 byte of type
  • 1 or more bytes of length
  • Value, where value can be
    • Integer data
    • ASCII string data
    • A sequence of things ..

For example

16304765 6E657261… is

  • Type 16 is an ASCII string
  • of length 0x30
  • with value 0x4765… which translates to EBCDIC Genera…

30068704 0A010102

  • Type 30 is a sequence (it happens to be a sequence of one element)
  • Of length 6
    • The value is a dotted decimal IP address of length 4
    • with value 0A010102 – which is 10.1.1.2.
Certificate 2
==========
Certificate algorithm :x509_alg_sha256WithRsaEncryption
Serial number Hex :68
Subject name :CN=10.1.1.2,OU=SSS,O=NISTEC256
Issuers name :CN=DocZosCA,OU=CA,O=COLIN
Not Before :26/04/05 00:00:00
Not After :27/04/05 23:59:59
Extension 1
============
Extension type:x509_ext_unknown
DER IA5String: Generated by the Security Server for z/OS (RACF)

Extension 2
============
Extension type:x509_ext_subjectAltName
DER Dotted address: 0x0A010102

Extension 3
============
Extension type:x509_ext_keyUsage
DER BitString: 0x0780
Sample output

Extension 4
============
Extension type:x509_ext_subjectKeyIdentifier
DER Octet: 0xF421DD55 F00BEE6E E81B97D2 1D5556AB A671EC59

Extension 5
============
Extension type:x509_ext_authorityKeyIdentifier
DER Context: 0xCAD12CE6 AACB598C 6E9595CD D812C90A C9C4C289

Example of using system ssl to sign some data

System SSL, also known as GS kit, provides an API for doing

  • TLS handshakes
  • Keyring manipulation
  • Encryption and signing of data

This blog post covers how to sign data. I wrote it because I could not find useful samples to get me started.

Signing data

To digitally sign data, you

  • perform a check sum calculation on the data,
  • encrypt the checksum with a private key,
  • package up make the data, the encrypted checkum, and the originator’s public certificate.

The recipient

  • checks the validity of the certificate
  • extracts the public key
  • decrypts the encrypted checksum
  • does a checksum of the enclosed data
  • compares the two checksums.

The checksums should match.

How the defining structures work together

Reference to fields

The definition of a record read from a keyring is

typedef struct _gskdb_record {
gskdb_record_type recordType;
gsk_octet recordFlags;
gsk_octet rsvd_1[1];
gsk_int32 recordId;
gsk_int32 issuerRecordId;
char * recordLabel;
union {
x509_certificate certificate;
pkcs_cert_request request;
} u;
pkcs_private_key_info privateKey;
gsk_octet rsvd_2[16];
} gskdb_record;

This has information about certificate, and the private key.

If you have a variable pRecord as a pointer to this structure, you can refer to the private key as pRecord->privateKey.

Because this structure can be used for both a certificate and a certificate request to refer to a certificate you have to use pRecord->u.certificate to locate the certificate.

Arrays of objects

Some API functions take one certificate, and others take an array of certificates. See Arrays of objects.

To sign some data

printf("....SIGN the data\n");
// pRecord points to the default record
gsk_buffer indata;
gsk_buffer outdata;
// Create the data to sign
indata.data = "ABCDEF";
indata.length = 6;
outdata.data ="OUTDATA "; // dummy data
outdata.length = 6 ;
// Define the certificate(s) to be used
pkcs_certificate cert;
cert.type = pkcs_certtype_certificate ;
cert.u. certificate = pRecord -> u.certificate;
pkcs_cert_key cert_key[1];
cert_key[0].certificate = cert;
cert_key[0].privateKey = pRecord -> privateKey;
// create our structure of an array of keys
// Note the structure is pkcs_cert_key>s< which contains
// one or more cert_key structures.
pkcs_cert_keys cert_keys;
cert_keys.count = 1;
cert_keys.certKeys = &cert_key[0];
// pkcs_certificates certs;
gsk_process_option po;
po.enforce_keyusage = 0;
po.enforce_content_length = 0;
po.enforce_keyparity = 0;
po.create_detached_signature = 0;
gskrc = gsk_make_signed_data_msg_extended
(po, // no options
0, // version
x509_alg_sha1Digest, // algoritm
1, // include certs in the output
&cert_keys, // the certs to use
0, // no ca certs
&indata,
0, // no attrib signers
&outdata);
if (gskrc == 0)
printf("gsk_make_signed_data_msg_extended worked\n");
else
{
printf("gsk_make_signed_data_msg_extended. %8.8x %s\n",
gskrc,gsk_strerror(gskrc));
return 8;
}
printf("Outdata length %i\n",outdata.length);
printHex(stdout,outdata.data,outdata.length);

This produced

00000000 : 3082048C 06092A86 4886F70D 010702A0  ................ .b.....f.f7..... 
00000010 : 82047D30 82047902 0100310B 30090605 ................ b.'.b.`.........
00000020 : 2B0E0302 1A050030 1506092A 864886F7 ................ ............f.f7
00000030 : 0D010701 A0080406 C1C2C3C4 C5C6A082 ................ ........ABCDEF.b
00000040 : 03023082 02FE3082 01E6A003 02010202 ................ ...b...b.W......
00000050 : 0168300D 06092A86 4886F70D 01010B05 ................ .......f.f7.....
00000060 : 00303031 0E300C06 0355040A 1305434F ................ ...............|
00000070 : 4C494E31 0B300906 0355040B 13024341 ................ <.+.............
...
00000460 : B6CBA3F3 48B5C9CD 76063F2D 007840A7 ................ ..t3..I....... x
00000470 : 0C8EF6FE 5442459E 47F6C1D0 12F1247E ................ ..6......6A}.1.=
00000480 : 0DC79B03 7723D129 078D1DB9 812F6EB9 ................ .G....J.....a.>.

In the data is

  • ABCDE – the original data
  • 3082048c – I recognise this as ANS-1 encoding. (ASN.1) is a standard interface description language (IDL) for defining data structures that can be serialized and deserialized in a cross-platform way. It is broadly used in telecommunications and computer networking, and especially in cryptography.
    • For example 048c is the length of data following

Once the signature has been obtained, it can be externalised (such as writing to a file) and sent to a remote site.

Because the data is in hexadecimal you may wish to convert it to EBCDIC for transport. You can use the

gsk_encode_base64 (gsk_buffer input_data,gsk_buffer_output_data);

to encode the data and use the gsk_decode_base64() to decode it.

Validate the data

The recipient can check that the signature and certificates are valid.

// Process the validation of the signature
// The list of input signatures
pkcs_certificates certs;
certs.count = 1;
certs.certificates = &cert;
gsk_attributes_signers as;
gsk_buffer temp; // process the data into temp.
printf("============READSIGNED======\n");
gsk_boolean ul; // were local certificates used?
gskrc = gsk_read_signed_data_msg_extended(
po, // no option
NULL, // no local certificates provided / local_certificates,
&outdata, // the signed data
&ul, // Set if certs were set locally or not
NULL, // returned cert not needed // msg_certificates
NULL, // signer_certificates
&as, // attrbute signers
&temp);
if (gskrc == 0)
{
printf("gsk_read_signed_data_msg_extended worked\n");
printf("Use local certificates %i\n",ul);
printHex(stdout,temp.data,temp.length);
}
else
{
printf("gsk_read_signed_data_msg_extended. %8.8x %s\n",
gskrc,gsk_strerror(gskrc));
return 8;
}

When this executed it produced

gsk_read_signed_data_msg_extended worked
Use local certificates 0
00000000 : C1C2C3C4 C5C6 ...... ABCDEF

which matches the original input data.

With

gskrc = gsk_read_signed_data_msg_extended(
po, // no option
NULL, // no local certificates provided / local_certificates,
...

Only certificates in the data were used. “Use local certificates” was 0.

When

gskrc = gsk_read_signed_data_msg_extended(
po, // no option
&certs, // use local certificates provided
...

it used the certificate(s) from my keyring. “Use local certificates” was 1.

My final code was

pkcs_certificates msgcerts;
pkcs_certificates signercerts;
gsk_attributes_signers as;
gskrc = gsk_read_signed_data_msg_extended(
po, // no option
&certs, // local_certificates,
&outdata, // stread output
&ul, // Set if certs were set locally or not
&msgcerts, // returned msg_certificates
&signercerts, // signer_certificates
&as, // attrbute signers
&temp);
if (gskrc == 0)
{
printf("gsk_read_signed_data_msg_extended worked\n");
printf("Use local certificates %i\n",ul);
printHex(stdout,temp.data,temp.length);
printf("====MSGCERTS+++\n");
// print_pkcs_certificates(&msgcerts);
print_certs(&msgcerts);
printf("====Signers++\n");
print_certs(&signercerts);
// print_pkcs_certificates(&signercerts);
}
else
{
printf("gsk_read_signed_data_msg_extended. %8.8x %s\n",
gskrc,gsk_strerror(gskrc));
return 8;
}

FIPS, TLS 1.3, AT-TLS, z/OS and not connecting.

Or, My TLS connection just dies during the handshake – because of FIPS!

I was working with John M. on a problem connecting a client machine to talk to z/OS TN3270, and this identified some “interesting” holes.

  • The root cause is that on z/OS 3.1 and earlier AT-TLS does not support FIPS with TLS 1.3.
  • There is support in z/OS 3.2 for FIPS 140-3.
  • The cards in ICSF need to be configured for FIPS. If they are not configured, the sessions will fail with a trace entry in the CTRACE output saying “FIPS not supported” or some other vague message.
  • You can use the operator command D ICSF,CARDS to display the status.
  • You can use the ISPF panels.
    • In ISPF option 6 type the command @ICSF. This displays the ICSF main panel.
    • Option 1 COPROCESSOR MGMT
    • It displays your co-processors.
    • Use the S line command on the co-processors
    • If you get a message like FIPS Compliance Mode          : NOT SUPPORTED. You need to reconfigure your co-processors.
  • To configured FIPS, it is a destructive reset, and all master keys will be reset. This needs to be carefully planned.

Steps to solving the problem

You can use tools like Wireshark to display the traffic, and sometimes see why a TLS handshake fails.

Many of the problems I experienced were due to configuration problems on z/OS. I got a CTRACE trace on z/OS, see GSK trace and TCPIP and this usually allowed me to fix the problem.

Alert (40)

Alert Message:Level: Fatal (2): Description: Handshake Failure (40)

I used the gsksrvr ctrace to find that I did not have any TLS 1.3 certificates in my configuration.

Alert (51)

With TLS 1.3, A certificate like

SUBJECTSDN(CN('10.1.1.2') - 
O('NISTEC256') -
OU('SSS')) -
ALTNAME(IP(10.1.1.2))-
NISTECC -
KEYUSAGE( HANDSHAKE ) -
SIZE(256 ) -
SIGNWITH (CERTAUTH LABEL('DOCZOSCA')) -
WITHLABEL('NISTEC256')

Failed. But changing it to SIZE(512) worked. Even though size 256 is supported.

Using TLS 1.3, the handshake to TN3270 failed with no reason.

I tracked down some problems due to FIPS being enabled.

FIPS standards establish requirements for ensuring computer security and interoperability, and are intended for cases in which suitable industry standards do not already exist.

I think of FIPS as taking the existing standards and making them a bit more secure. For example not allowing some cipher suites. Not allowing certificates with small keys.

Enabling FIPS properly does not look easy. For example the documentation says it requires that load modules are cryptographically signed, so code authorised programs can check they have not been changed. Under the covers I believe that when IBM ships a module, it calculates the hash of the code, then encrypts the hash, and stores the encrypted has within the loadmodule. At runtime you use IBM’s public key to decrypt this value; does the same hash on the module, and compares this.

Once this has been done, you can add statements to the ICSF configuration, such as FIPSMODE(YES,FAIL(YES)).

This says use FIPS, and if any checking fails – fail the request.

In z/OS 3.2 there is FIPS support for TLS 1.3 see option FIPSMODE(140-3,INDICATE,FAIL(fail-option))

Not all configurations are supported

The TLS 1.3 ciipher suites, ChaCha20 and ChaCha20-Poly1305 are not supported by FIPS. You need to use cipher suites, configured with AES-GCM or AES-CCM.

I ran my test using FIPS

I could see in Wireshark that there was the TLS 1.3 trace

  • ClientHello request going to the server
  • ServerHello coming from the server
  • Change Cipher spec coming from the server
  • and nothing. No Alert message.

I found an entry in the z/OS 2.5 documentation.

The FIPS 140-2 standard does not define support for TLSv1.3 or the new cipher suites defined for it. Enabling both the TLSv1.3 protocol and FIPS support results in an error.

When my request failed I got CTRACE entries like

S0W1      MESSAGE   00000004  12:52:55.333904  SSL_ERROR                                  
Job TCPIP Process 0201001E Thread 00000001 crypto_chacha_encrypt_ctx
ICSF service failure: CSFPSKE retCode = 0x8, rsnCode = 0xbfe

S0W1 MESSAGE 00000004 12:52:55.334123 SSL_ERROR
Job TCPIP Process 0201001E Thread 00000001 crypto_chacha_encrypt_ctx
The algorithm or key size is not supported by ICSF FIPS

S0W1 MESSAGE 00000004 12:52:55.334355 SSL_ERROR
Job TCPIP Process 0201001E Thread 00000001 gsk_encrypt_tls13_record
ChaCha20 Encryption failed: Error 0x0335308f

Where the return code 0xbfe is

The PKCS #11 algorithm, mode, or keysize is not approved for ICSF FIPS 140-2. This reason code can be returned for PKCS #11 clear key requests when ICSF is in a FIPS 140-2 mode or 140-3,HYBRID mode. To see how 8/BFE(3070) can be returned when the ICSF FIPSMODE is 140-3,HYBRID, see ‘Requiring FIPS 140-2 algorithm checking from select z/OS PKCS #11 applications’ in z/OS Cryptographic Services ICSF Writing PKCS #11 Applications.

May the FIPS code is badly implemented, by not producing an alert message such as “FIPS processing problem”, but some security products to not display error information, because it makes it easier to break in!

The hidden startup options for httd on z/OS

I had clearly configured http on z/OS, as when it started it printed Colins startup, but I could not find where this was being executed from.

I eventually found it was in the home directory for httpd. My http server starts with PARM=’SH /usr/lpp/ihsa_zos/bin/apachectl…’. In /usr/lpp/ihsa_zos/bin/envvars are the magic startup parameters, including some gsktrace settings.

I have

#!/bin/sh 
.  /usr/lpp/ihsa_zos/bin/cleanup.sh 
IHS=/usr/lpp/ihsa_zos 
LIBPATH=$IHS/lib:$IHS/modules:$IHS 
PATH=$IHS/bin 
_EDC_ADD_ERRNO2=1 
_BPX_SHAREAS=NO 
_BPX_BATCH_SPAWN=YES 
GSK_SSL_HW_DETECT_MESSAGE=1 
LC_ALL=En_US.IBM-1047 
#rm /u/mqweb3/conf/*.log 

export GSK_TRACE=0x00 
export GSK_TRACE=0xff 
export GSK_TRACE_FILE=/u/mqweb3/conf/httpd.gsktrace 
#xport GSK_SERVER_TLS_KEY_SHARES=00300029002500240023 
#export GSK_TLS_SIG_ALG_PAIRS=0601050104010301080608050804050304030603 
#export GSK_TLS_CERT_SIG_ALG_PAIRS=0601050104010301080608050804050304030603 f

and /usr/lpp/ihsa_zos/bin/cleanup.sh has

#!/bin/sh 
# 
echo 'colins cleanup' 
rm /u/mqweb3/conf/*.log 
rm /u/mqweb3/conf/httpd.gsktrace 

It is always easy when you know the answer.

Putting the GSK_TRACE information in this file is not recommended as it will trace every system SSL call, and to turn it off, you have to stop http, edit the file to say GSK_TRACE=0x00 and restart the server. This article describes how to collect a trace using CTRACE. You can turn trace on and off without restarting the HTTP server.

To format this gsk trace I had to use the following command in Unix Services

gsktrace /u/mqweb3/conf/httpd.gsktrace > gsktrace.txt

How do I format an encoded name from a digital certificate?

I had a string CN=myserver,O=test,C=us in a certificate, which has been encoded

Offset   :                                       EBCDIC          ASCII 
00000000 : 302F310B 30090603 55040613 02757331  ................ 0/1.0...U....us1  
00000010 : 0D300B06 0355040A 13047465 73743111  ................ .0...U....test1.  
00000020 : 300F0603 55040313 086D7973 65727665  ........._`..... 0...U....myserve  
00000030 : 72                                  .                r                 
    

where you can see the encoded string in ASCII on the right. How do I extract the string CN=myserver,O=test,C=us from this?

This format is used for Subjects and Issuers (and other types).

The hex string has an ASN.1 encoding. It usually starts with a 0x30. See here for a good introduction to ASN.1 encoding.

  • 30 means this is a sequence
  • 2f is the length of the data following
  • etc

On z/OS you can use system ssl to decode this back to a printable string. You need two steps.

  1. Convert to internal format,
  2. Create the printable EBCDIC string from the internal format.

Step1 create internal format of the data

#pragma runopts(POSIX(ON)) 
#include <gskcms.h> 
#include <gskssl.h>  
void x509_to_string(char * pData, // pointer to the data
                     int lData)    // length of the data
{ 
   x509_name    X509;   // internal format.
   int rc; 
   gsk_buffer cert;     // intermediate format
   if (lData == 0) 
   { 
      printf("no data"); 
      return; 
   } 
   cert.length=   lData; 
   cert.data  =   pData ; 
   // convert from 0x302F310B 30090603 to internal 
   rc = gsk_decode_name( 
       & cert, 
       & X509); 
   if ( rc != 0) 
   { 
     //use gsk_strerror(rc) to return a string.
     printf("\nInternal error:gsk_decode_name %s\n", 
                  gsk_strerror(rc)); 
     return; 
   } 

Convert from internal format to an EBCDIC string.

   char * pName ;       // output value   
   // convert from internal to CN=COLIN,C=GB 
   rc = gsk_name_to_dn( 
              &X509, 
              &pName); 
   // free the intermediate value regardless of rc
   gsk_free_name(&X509); 
   if ( rc != 0) 
     { 
       printf("\nInternal error:gsk_name_dn %s\n", 
                    gsk_strerror(rc)); 
       return; 
     } 
   printf("%s.",pName); 

   //free the string 
   gsk_free_string(pName); 
} 

Convert from EBCDIC string to intermediate or certificate format.

There are the reverse functions.

  • gsk_dn_to_name( pName, &X509);
  • rc = gsk_encode_name( &X509, & cert);

Both C=GB,CN=colin and c=GB,cn=colin produce

00000000 : 301D310E 300C0603 55040313 05636F6C …………..?% 0.1.0…U….col
00000010 : 696E310B 30090603 55040613 024742 .>…………. in1.0…U….GB

JCL

I needed binder input options.

INCLUDE GSK(GSKCMS31) 
INCLUDE GSK(GSKSSL) 

and JCL

//COMPILE  EXEC PROC=EDCCB, 
//       LIBPRFX=&LIBPRFX, 
//       CPARM='OPTFILE(DD:SYSOPTF)', 
// BPARM='SIZE=(900K,124K),RENT,LIST,RMODE=ANY,AMODE=31,AC=1' 
//COMPILE.SYSLIB DD DISP=SHR,DSN=&LIBPRFX..SCEEH.SYS.H 
//               DD DISP=SHR,DSN=&LIBPRFX..SCEEH.H 
//               DD DISP=SHR,DSN=SYS1.SIEAHDR.H 
...
//COMPILE.SYSIN  DD DSN=&SOURCE(&PROG),DISP=SHR 
//COMPILE.SYS    DD DISP=SHR,DSN=&LIBPRFX..SCEEH.SYS.H 
//COMPILE.SYSOPTF DD DISP=SHR,DSN=&SOURCE(CCOPTS) 
//* 
//* Bind the module 
//* 
//BIND.SYSLMOD DD DISP=SHR,DSN=&LOADLIB(&PROG) 
//BIND.SYSLIB  DD DISP=SHR,DSN=&LIBPRFX..SCEELKED 
//             DD DISP=SHR,DSN=SYS1.SIEALNKE 
//BIND.GSK     DD DISP=SHR,DSN=SYS1.SIEALNKE 
...
//BIND.SYSIN DD DISP=SHR,DSN=&SOURCE(&BINDOPTS) 

Where the gskcms.h and gskssl.h are in DSN=SYS1.SIEAHDR.H or /usr/include/, and the binder stubs are in DSN=SYS1.SIEALNKE.

GSK trace and TCPIP

To track down a TLS setup problem with TN3270 and AT-TLS, I had to collect a System SSL Trace. You can collect data and have it output to the joblog, or the console. This is not good practice as it can produce a lot of output, and it is hard to read the data.

This blog post follows on from Collecting a TCPIP Packet trace on z/OS.

You can use the same CTRACE writer procedure for all your CTRACE work. It is better not to have more than one trace writing to it at a time.

Erik Janssen sent me an email on how to take the CTRACE file and process it in Wireshark. I’ve put his comments at the bottom.

You need to start the GSK Server task

See SSL Server task

My GSKSRVR JCL looks like

//GSKSRVR  PROC  REGSIZE=256M,OUTCLASS='A' 
//*-------------------------------------------------------------------- 
//GO       EXEC  PGM=GSKSRVR,REGION=&REGSIZE,TIME=1440, 
//  PARM=('ENVAR("HOME=/etc/gskssl/server"),TERM(DUMP)                 X
//             / 1>DD:STDOUT 2>DD:STDERR') 
//STDOUT   DD  SYSOUT=&OUTCLASS,DCB=LRECL=250, 
//  FREE=END,SPIN=UNALLOC 
//STDERR   DD  SYSOUT=&OUTCLASS,DCB=LRECL=250, 
//  FREE=END,SPIN=UNALLOC 
//SYSOUT   DD  SYSOUT=&OUTCLASS, 
//  FREE=END,SPIN=UNALLOC 
//CEEDUMP  DD  SYSOUT=&OUTCLASS, 
//  FREE=END,SPIN=UNALLOC 

I did not change any parameters, or defaults.

Start it

s GSKSRVR

It gives

GSK01001I System SSL version 4.4, Service level OA59115.
GSK01003I SSL server initialization complete.

Within the TELNET (TN3270) parameters I had

TelnetParms         ; ATTLS defined secure port 
 TTLSPort 2023      ; 
 DEBUG CONN DETAIL CTRACE 
 DEBUG CONN trace  CTRACE 
 DEBUG TRACE CTRACE
 ConnType Secure    ; Client chooses secure or nonsecure connection. 
EndTelnetParms 

This limits the trace to only port 2023, other ports, such as 0023 were unaffected.

I set up parmlib member for GSK trace, member CTGSKON

TRACEOPTS 
          WTRSTART(ctwtr) 
          on 
          jobname(TCPIP) 
          wtr(ctwtr) 
          OPTIONS('LEVEL=255') 

I turned trace on using

TRACE CT,ON,COMP=GSKSRVR,PARM=CTGSKON

This gave

IEE252I MEMBER CTGSKON FOUND IN USER.Z24C.PARMLIB
GSK01040I SSL component trace started.
ITT038I ALL OF THE TRANSACTIONS REQUESTED VIA THE TRACE CT COMMAND WERE SUCCESSFULLY EXECUTED.

ITT110I INITIALIZATION OF CTRACE WRITER CTWTR COMPLETE.

The CTWTR procedure was started was part of this command.

You stop the trace with

trace CT,off,COMP=GSKSRVR

Wait for the message

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

And stop the CTWTR writer using

TRACE CT,WTRSTOP=CTWTR

You should get messages

ITT038I ALL OF THE TRANSACTIONS REQUESTED VIA THE TRACE CT COMMAND WERE SUCCESSFULLY EXECUTED.
IEE839I ST=(ON,0001M,00005M) AS=ON BR=OFF EX=ON MO=OFF MT=(ON,064K)
IEF196I AHL904I THE FOLLOWING TRACE DATASETS CONTAIN TRACE DATA :
IEF196I IBMUSER.CTRACE1
AHL904I THE FOLLOWING TRACE DATASETS CONTAIN TRACE DATA :
IBMUSER.CTRACE1

ITT111I CTRACE WRITER CTWTR TERMINATED BECAUSE OF A WTRSTOP REQUEST.

Process 0201003E Thread 00000001 edit_ciphers
Using server certificate ‘ZZZZ’

S0W1 MESSAGE 00000008 15:19:52.223132 SSL_INFO
Job TCPIP Process 0201003E Thread 00000001 read_v3_extended_client_hello
Elliptical curve 0030 is being skipped as it is only supported in TLS 1.3

Setting up PFKeys

I set up some PFKeys to make it easier to issue these commands

In User.parmlib(PFKTAB00) I have

...
PFKTAB TABLE(CTRACE)
PFK(4) CMD('S GSKSRVR') CON(Y)
PFK(5) CMD('TRACE CT,ON,COMP=GSKSRVR,PARM=CTGSKON')
PFK(6) CMD('TRACE CT,OFF,COMP=GSKSRVR')
PFK(7) CMD('TRACE CT,WTRSTOP=CTWTR')

I activate these keys using

K N,PFK=CTRACE
D PFK

                                                                  
IEE235I 10.05.03 PFK DISPLAY 729
PFK DEFINITIONS FOR CONSOLE L700 - TABLE=CTRACE IN PFKTAB00
KEY# CON ------------------------- DEFINITION ------------------------
1 NOT DEFINED
2 NOT DEFINED
3 NOT DEFINED
4 YES S GSKSRVR
5 NO TRACE CT,ON,COMP=GSKSRVR,PARM=CTGSKON
6 NO TRACE CT,OFF,COMP=GSKSRVR
7 NO TRACE CT,WTRSTOP=CTWTR

Format the data using IPCS

If there is data in the file, you can use IPCS to format it.

The IPCS command is

CTRACE COMP(GSKSRVR) FULL ALL

You get output like

S0W1      MESSAGE   00000008  15:19:52.221020  SSL_INFO                 
Job TCPIP Process 0201003E Thread 00000001 read_v3_client_hello
Received CLIENT-HELLO message

S0W1 DUMP 00000020 15:19:52.221414 SSL_ASCII_DUMP
Job TCPIP Process 0201003E Thread 00000001 read_v3_client_hello
CLIENT-HELLO message
00000000: 010001FC 03030C33 81E156C3 DF4693DD *.......3..V..F..*
00000010: 621CA636 8A75BDF0 26F3DE8F 831B78BC *b..6.u..&.....x.*
...
S0W1 MESSAGE 00000008 15:19:52.226897 SSL_INFO
Job TCPIP

Using batch IPCS

I used the following JCL

//IBMIPCS JOB 1,MSGCLASS=H 
//S1 EXEC PGM=IKJEFT01,REGION=0M
//STEPLIB DD DISP=SHR,DSN=SYS1.MIGLIB
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSPROC DD DISP=SHR,DSN=USER.Z31B.CLIST
// DD DISP=SHR,DSN=ADCD.Z31B.CLIST
// DD DISP=SHR,DSN=SYS1.SBLSCLI0
//IPCSPARM DD DISP=SHR,DSN=SYS1.PARMLIB
// DD DISP=SHR,DSN=ADCD.Z31B.PARMLIB
//IPCSTOC DD SYSOUT=*
//IPCSDDIR DD DISP=SHR,DSN=SYS1.DDIR
//SYSTSIN DD *
IPCS NOPARM
SETDEF DA('IBMUSER.CTRACE1')
DROPD
CTRACE COMP(GSKSRVR) FULL ALL
/*

Getting the data into Wireshark format.

Wireshark is an excellent tool for doing network trace, and decoding the flows. You can also is it to process the CTRACE data. Thanks for Eril Janssen for the instructions below on how to get from CTRACE to Wireshark.

You need to use the CTRACE SNIFFER option. This writes to the file with DDNAME of SNIFFER

Allocate a VB 1600 LRECL dataset and allocate it to a ddname, for example SNIFFER
ALLOC DD(SNIFFER) DS(<some dsname>) SHR
From IPCS you can now:

CTRACE COMP(SYSTCPDA) GMT SHORT  OPTIONS((SNIFFER(1600,TCPDUMP) NOREASSEMBLY)) DS(‘<the trace ds>’)


The formatted trace will now be in <some dsname>. Do a binary transfer to your pc and you show be able to open the trace in wireshark.

I tend to use IPCS in batch (so I do not get the lock on the trace file, (grin)).

Problems

I was using CTRACE, the IPCS, then CTRACE etc. Sometimes the CTRACE writer would not start (or stop). This can be because you are still in IPCS, and IPCS has a lock on the dataset, or you have quit from IPCS, and TSO still has a lock on the data set. I used =X to get out of the ISPF session, and this usually freed it up.

I could it was easiest to run IPCS in batch – it saved lots of typing, and starting/stopping CTRACE when I had got it wrong.

There may still be a timing window, where the trace writer would not stop. When I stopped gsksrvr, the trace writer stopped.