User Tools

Site Tools


pki:creating_and_renewing_client_certificates

Creating and Renewing self signed X509 certificates automatically

This process is designed to be automatic, so that an IoT device can download, install and renew its own X509 client certificates.

Pre-requisites

You have installed the PKI software You have created and installed the Certificate Authority (CA)

Certificate Request API

The device must make an API call, and receives a token which will be used to download the certificate once approved.

Example:

POST /IoT_pki/new_request/ HTTP/1.1
Accept: application/json
Content-Type: application/json

{
    "country_name": "",
    "state_or_province_name": "",
    "locality_name": "",
    "organization_name": "",
    "organization_unit_name": "",
    "email_address": "",
    "dns_name": "",
    "common_name": "obligatoryUniqueName",
    "dn_qualifier": ""
}

Response:

HTTP 201 Created
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "country_name": "",
    "state_or_province_name": "",
    "locality_name": "",
    "organization_name": "",
    "organization_unit_name": "",
    "email_address": "",
    "dns_name": "",
    "common_name": "obligatory",
    "dn_qualifier": "",
    "approved": false,
    "token": "BJUPPF2DUJ4INPSGRZY3"
}

Authorization of request

The PKI administrator must authorize new certificate requests by logging in to the PKI administration panel.

Certificate Download API

The device must make an API call submitting the token received from the certificate request made earlier. Example:

GET /IoT_pki/cert_collect/<token>/ HTTP/1.1

Responses:

HTTP 201 Created
Content-Type: application/x-pem-file

Automatic Certificate Renewal

The device must make an API call using a valid client certificate.

Example:

GET /IoT_pki/renew_cert/ HTTP/1.1

Responses:

HTTP 201 Created
Content-Type: application/x-pem-file

The pem file contains both public and private keys of the new client certificate in one pem file.

Example Client Code in Python

The following code is an example of how the complete api could be implemented in a device. Typically the function below could be called every 24 hours from cron job.

from requests import Request, Session
import requests
import json
import sys
import os   


def processRequest():                    
        #tries to renew certificate if available, if not makes request to obtain new certificate
        #returns True if valid certificate available
        
        
        if (renewClientCert(pathToClientCert)):
            return True
       
        try:
            token=readTokenFromFile()
            tokenIsValid=True
        except:
            tokenIsValid=False
            
        while tokenIsValid:
        #if we have a valid token we will try to collect certificate, until we get an invalid token response from the server
        #
            result=collectCertWithToken(token)
            if(result.status_code==201):
                os.remove(pathToToken)#remove used token
                logger.info("collected cert with token - test renewal before exiting")
                #we should have valid cert here, but return False to force test renewal before exiting
                return False
            elif (result.status_code==404):
                logger.info('token not found')
                tokenIsValid=False
                os.remove(pathToToken)
            else:
                #unapproved token requests return 403 token is kept
                logger.debug('token is not approved, go to admin panel and approve cert_request')
                return False
                    
                
            
        logger.info('making new cert request')
        postdata={'common_name':device_common_name}
        headers = {'Accept': 'application/json',
                   'Content-Type' : 'application/json'}
    
        result=callPKI ('/IoT_pki/new_request/','POST',headers,postdata,)
        try:
            data=result.json()
        except:
            pass
        if (result.status_code==201):
            logger.info('obtained request token')
            token=data['token']
            writeTokenToFile(token)
        else:
            logger.error('unable to request new certificate')    
        return False


def renewClientCert(pathToClientCert):
    logger.info('checking for presence of certificate file')
        
    if (os.path.isfile(pathToClientCert)):
        
        result=callPKI ('/IoT_pki/renew_cert/','GET')
        if (result.status_code==201):
            with open(pathToClientCert,"wb") as f:
                f.write(result.content)
                logger.info('certificate renewed..exiting')
                return True
        
        elif (result.status_code==200):
            logger.info('certificate valid but not due for renewal..exiting')
            return True
        else:
            logger.info(result.content)        
    else:
        logger.info('no client certificate found')
    return False

    
                
def writeTokenToFile(token):
    with open(pathToToken,"w") as f:
        f.write(token)
        f.close()

def readTokenFromFile():
    with open(pathToToken,"r") as f:
        token= f.read()
        f.close()
        logger.debug("token:%s",token)
        return token    
            
            
def collectCertWithToken(token):
        #returns result to allow us to decide what to do as function of status code
        apiurl="/IoT_pki/cert_collect/"+str(token)+"/"
        
        result=callPKI(apiurl,'GET')
        if (result.status_code==201):
            with open(pathToClientCert,'wb') as f:
      
    # write the contents of the response (r.content)
    # to a new file in binary mode.
                f.write(result.content)
                logger.info('client certificate saved')
            
        return result
        

def callPKI(apiurl,callType,headers={},data={}):

       
    if settings.PKI['verify_certs']:
            #TO DO   need to distinguish between ca cert for ssl and pki!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        verifycerts= settings.PKI['path_to_ca_cert'] 
    else:
        verifycerts=False
                
    url='https://'+settings.PKI['host']+":"+str(settings.PKI['port'])+apiurl
    logger.debug('making %s request to pki %s',callType,url)
    username= settings.PKI['user']
    password= settings.PKI['password']
        
    s = Session()
    req = Request(callType,url,data=json.dumps(data),headers=headers,auth=(username,password))
    prepped = s.prepare_request(req)
    
    # if we have Client Cert use it
    if (os.path.isfile(pathToClientCert)):
        cert=pathToClientCert
    else:
        cert=None
        
    result = s.send(prepped,verify=verifycerts,cert=cert)
    logger.info("status code received %s" , result.status_code)
            
    return result  

pki/creating_and_renewing_client_certificates.txt · Last modified: 2017/07/20 12:18 by matt