How to consume the Credential store with python

For those interested in a secure repository for passwords and keys for applications running on SAP Business Technology Platform, the Credential store is the way to go. This blog describes very well how to set up the service, so I won’t repeat these steps here.

The aim of this post is to explain how to consume this service with Python 3. The blog mentioned above describes how to consume the service with Node.js. You can find in the official SAP API documention how to consume it with Java, Javascript, Swift, Curl, ABAP and SAPUI5. A GoLang sample is also available in this other documentation. Almost all languages but Python.

The context of this work is the management the HANA Cloud instance service (start and stop). For this task, one need to use the credentials of a Cloud foundry manager and we don’t want these written in plain text. Since updating a Cloud Foundry service with Python would be another topic, I will only expose here how to consume the Credential store.

The core python libraries are requests and python-jose, which have Apache 2-0 and MIT license respectively.

All of what follow could be written nicely in a function but I want to emphasise what do what.

After creating the service mycreds (free plan), we add in the namespace test the password Azerty for id_conn.

Let’s start with the libraries:

import json
from cfenv import AppEnv
from jose import jwe
import requests
from requests.auth import HTTPBasicAuth

Since the app will be deployed in the same Space as the service, one can retrieve the environment variable (VCAP_SERVICES) easily with cfenv

env = AppEnv()
creds = env.get_service(name="mycreds") #name of the Cred.Store service

So creds here is a dictionary with the parameters of the service. Then, we call the API of this service to get the password associated with id_conn.  

headers = {'Accept': 'application/json', 'Content-Type': 'application/jose', 'DataServiceVersion': '2.0', 'If-None-Match': '', 'sapcp-credstore-namespace': 'test'} # name of the namespace response = requests.get(creds.credentials.get('url')+"/password?name=id_conn", headers=headers, auth=HTTPBasicAuth(creds.credentials.get("username"), creds.credentials.get("password")) )

The response is a JSON Web Encryption (JWE) with a four parts structure: Header, Encrypted Key, Ciphertext and the JWE Integrity Value.

The first part of the JWE will tell you something like

{ "alg": "RSA-OAEP-256", "enc": "A256GCM", "iat": 1631885785

The message encryption is implemented via symmetric encryption using Advanced Encryption Standard (AES), Galois Counter Mode (GCM) with 256 bit key size. The encryption of keys is supported using RSA Optimal Asymmetric Encryption Padding (OAEP).

The private key is given in the parameters but one needs to run some transformations before using it: first add header and footer and then convert in bytes:

private = creds.credentials.get("encryption",{}).get("client_private_key")
private_key = ("-----BEGIN PRIVATE KEY-----\n" + private + "\n-----END PRIVATE KEY-----")
private_key = private_key.encode()

Finally, one can decode the message and get the password

cred = jwe.decrypt(response.content, private_key)
cred = json.loads(cred.decode())
pwd= cred.get("value")

Another key can be useful : cred.get("modifiedAt") which indicates the last modification.

If you want to “see” the password in a web application, you can encapsulate the code in this script

# -*- coding: utf-8 -*-
import os
from flask import Flask ### Add the previous lines here #### @app.route("/")
def locale(): return pwd if __name__ == "__main__":'', port=port)

You should get :

As you can see, it’s pretty easy to store and consume a password in a secured manner using this service. We have not cover the other functionalities here (like updating a password) but the main difficulties, from my point of view, are to know which python library to use for the encryption and how to format the API call. If some people are interested, I may write a follow up for the other functionalities.

That’s all for today !