IBM SDK for Node.js – z/OS® is a Javascript runtime that provides a secure, module-driven, highly scalable approach to accelerate digital transformation on IBM Z.

When designing HTTP-based APIs such as REST APIs that communicate sensitive data, such as bank account information or emails over the network, it is critical to design APIs that are secure. The HTTPS protocol is the standard protocol used for secure communication between client and server. In a nutshell, the HTTPS protocol encrypts data sent over the network, preventing man-in-the-middle attacks, where network packets can be sniffed for sensitive information.

Goal

This article will demonstrate how to leverage a z/OS RACF® key ring to establish a secure HTTPS-based REST API on z/OS.

If you’re not familiar with RACF key rings, they can best be described as a collection of digital certificates, which typically contain a private and public key pair, as well as a CA certificate or self-signed certificate. IBM defines a key ring as such:

A key ring is a collection of certificates that identify a networking trust relationship (also called a trust policy). In a client-server network environment, entities identify themselves using digital certificates. Server applications on z/OS that want to establish network connections to other entities can use RACF key rings and other related services to determine the trustworthiness of the client or peer entity.

The public and private keys in the RACF key ring are used to encrypt and decrypt the transmitted data, while the certificate is used for validation or trust, typically signed by a CA authority (often associated with a cost). In this article, we will generate a self-signed certificate, which has no cost associated with it.

We will then make use of the zcrypto node module to interface with RACF key rings and the express node module to establish a secure HTTPS REST API.

Prerequisites

Download IBM SDK for Node.js – z/OS, V8 or greater and follow the Knowledge Center Documentation installation instructions to install IBM SDK for Node.js – z/OS.

Step 1: Generating a self-signed certificate and public/private key pair in RACF

The RACF facility allows you to generate a RACF key ring consisting of a public/private key pair and a certificate. There are a few ways that you can create a RACF key ring. In this article, we write a JCL script to create a RACF key ring. The JCL script will perform the following tasks:

  1. Create the self-signed certificate with label CACert that expires in 2030, using the RACF command RACDCERT GENCERT CERTAUTH
  2. Create the private/public key pair with label ServerCert that expires in 2030, using the RACF command RACDCERT GENCERT
  3. Create a RACF Key ring named MYRING, using the RACF command RACDCERT ADDRING
  4. Connect private/public key pair 'ServerCert' and self-signed certificate 'CACert' to RACF key ring MYRING, using the RACF command RACDCERT ID(USERID) CONNECT

Create a new file named gencerts.jcl, and open it in a text editor of your choosing. 

Write the following JCL to the file:

//*gencerts.jcl
//USERIDX  JOB X,X,USER=USERID,REGION=4M,MSGCLASS=H,TIME=1440
//*
//S1       EXEC PGM=IKJEFT01
/*Modify /tmp/gencert_output.txt below if necessary
//SYSTSPRT DD PATH='/tmp/gencert_output.txt',
//            PATHOPTS=(OCREAT,ORDWR,OTRUNC),
//            PATHMODE=(SIRUSR,SIRGRP,SIROTH,SIWUSR,SIWGRP,SIWOTH),
//            FILEDATA='TEXT',BLKSIZE=32760
//SYSPRINT DD SYSOUT=*
//SYSTSIN  DD *

RACDCERT GENCERT CERTAUTH +
SUBJECTSDN( +
  CN('Hostname') +
  C('Country') +
  SP('Province') +
  L('City') +
  O('Company') +
  OU('Department') +
) +
ALTNAME( +
  EMAIL('test@ibm.com') +
  DOMAIN('hostname.com') +
) +
WITHLABEL('CACert') +
NOTAFTER(DATE(2030/01/01))

RACDCERT GENCERT ID(USERID) +
SUBJECTSDN( +
  CN('Hostname') +
  C('Country') +
  SP('Province') +
  L('City') +
  O('Company') +
  OU('Department') +
) +
ALTNAME( +
  EMAIL('test@ibm.com') +
  DOMAIN('hostname.com') +
) +
WITHLABEL('ServerCert') +
SIGNWITH(CERTAUTH LABEL('CACert')) +
NOTAFTER(DATE(2030/01/01))

RACDCERT ADDRING(MYRING) ID(USERID)

RACDCERT ID(USERID) CONNECT(CERTAUTH LABEL('CACert') +
     RING(MYRING))

RACDCERT ID(USERID) CONNECT(ID(USERID) LABEL('ServerCert') +
     RING(MYRING) USAGE(PERSONAL) DEFAULT)

Prior to submitting the JCL script gencerts.jcl, replace all instances of USERID with your z/OS userid, as well as any relevant information (email, domain, country, hostname, province, etc.).

The relevant output of the JCL job will be stored into HFS path: /tmp/gencert_output.txt.

To submit the JCL script under z/OS UNIX System Services, enter the following command:

submit gencerts.jcl

The output file /tmp/gencert_output.txt should now be generated. If it is not, check your SDSF job log for more details.

Open /tmp/gencert_output.txt with your editor of choosing. The contents should be as follows:

1READY
 READY
 RACDCERT GENCERT CERTAUTH SUBJECTSDN( CN('Hostname') C('Country') SP('Province') L('City') O('Company') OU('Department') ) ALTNAME( EMAIL('test@ibm.com') DOMAIN('hostname.com') ) WITHLABEL('CACert') NOTAFTER(DATE(2030/01/01))
 IRRD175I The new profile for DIGTCERT will not be in effect until a SETROPTS REFRESH has been issued.
 READY
 READY
 RACDCERT GENCERT ID(USERID) SUBJECTSDN( CN('Hostname') C('Country') SP('Province') L('City') O('Company') OU('Department') ) ALTNAME( EMAIL('test@ibm.com') DOMAIN('hostname.com') ) WITHLABEL('ServerCert') SIGNWITH(CERTAUTH LABEL('CACert')) NOTAFTER(DATE(2030/01/01))
 IRRD175I The new profile for DIGTCERT will not be in effect until a SETROPTS REFRESH has been issued.
 READY
 READY
 RACDCERT ADDRING(MYRING) ID(USERID)
 READY
 READY
 RACDCERT ID(USERID) CONNECT(CERTAUTH LABEL('CACert') RING(MYRING))
 READY
 READY
 RACDCERT ID(USERID) CONNECT(ID(USERID) LABEL('ServerCert') RING(MYRING) USAGE(PERSONAL) DEFAULT)
 READY
 END

This output indicates that the public key, private key, and self-signed certificate were generated and connected to the RACF key ring MYRING successfully.

If you notice a message as follows:

You are not authorized to issue the RACDCERT command

Then you will need to grant CONTROL access to the RACF facilities IRR.DIGTCERT.GENCERT, IRR.DIGTCERT.ADD, IRR.DIGTCERT.ADDRING, and IRR.DIGTCERT.CERTAUTH. Resubmit the JCL script after granting the necessary access.

Step 2: Starting a New Project

Start a terminal session and connect to your z/OS UNIX System Services environment.  
Create a directory named my-secure-api for your project and change your current working directory to it.  

mkdir my-secure-api
cd my-secure-api

Use npm to create a Node.js package as follows:

npm init --yes

This command will create a package.json file which describes your package and its dependencies. The file will contain the following:

{
  "name": "my-secure-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

By default, the main source file for our project is index.js.

Step 3: Installing zcrypto and express node dependencies

The next step is to install the zcrypto node module (module to interface with RACF key rings) and express node module (module to create REST APIs) as dependencies for our project as follows:

npm install zcrypto express

This command will generate a new file package-lock.json, as well as modify package.json to add zcrypto and express as project dependencies.

Step 4: Create a secure HTTPS REST API with express and zcrypto node modules

We will now leverage the zcrypto and express node modules to create a secure REST API.

Create a new file named index.js, and open it in a text editor of your choosing. 

Write the following code to the file:

const https = require('https');
const fs = require('fs');

const zcrypto = require('zcrypto');
const express = require('express');

const port = 3000;

const racf_key_ring = "MYRING"; // Modify to your RACF key ring name

// Create an key ring object
var keyring = new zcrypto.ZCrypto();

// Open RACF key ring
if (keyring.openKeyRing(racf_key_ring)) {
    console.error("Could not open key ring")
    process.exit(1);
}

// Export Private Key Certificate labelled 'ServerCert' to pem object
// pem object has two elements {key, cert}
var pem = zcrypto.exportLabelToPEM(keyring, "ServerCert");

var options = {
  key: pem.key,
  cert: pem.cert
};

// Create express application
app = express();

// Create REST API on root URI
app.get('/', (req, res) => {
   res.send('Now using https..');
});

// Create HTTP server with SSL/TLS key/certificate
var server = https.createServer(options, app);

server.listen(port, () => {
  console.log("server starting on port : " + port);
});

The above source code begins by creating a key ring object using the zcrypto class and then opens a handle to the RACF key ring MYRING using the method openKeyRing.
The private key ‘ServerCert’ is then converted to the Node.js standard PEM DER certificate format, via a call to exportLabelToPEM.
The resulting PEM key/cert pair object is then passed as an argument in https.createServer to create a secure HTTPS REST API server.

Step 5: Running the application

Run the index.js code with the Node.js runtime using the following command:

node index.js

If the RACF key ring provided was valid, the following message will be displayed:

server starting on port : 3000

Our secure web server is now listening on port 3000.

Now, using curl, send a request to your REST API:

curl -k https://:3000/

To avoid using -k (–insecure), you can copy the RACF self-signed certificate to a cacert file and rerun with –cacert:

curl --cacert ca.cert https://:3000/

You should get the following response:

Now using https..

Congratulations, you have created a secure HTTPS REST API using RACF key rings!

Join The Discussion

Your email address will not be published. Required fields are marked *