{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Multi-Region IoT - Request Device Certificate from PCA\n", "\n", "Create a key and CSR. The CSR will be send to PCA which issues a certificate to be used for a virtual IoT device. Private, public key as well as the certificate are stored on the local file system." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from OpenSSL import crypto, SSL\n", "from os.path import exists, join\n", "from os import makedirs\n", "from shutil import copy\n", "from time import time, gmtime, localtime, strftime\n", "import boto3\n", "import json\n", "import time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Global Vars" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%store -r config\n", "print(\"config: {}\".format(json.dumps(config, indent=4, default=str)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def createCertRequest(pkey, subject, digest=\"sha256\"):\n", " print(\"subject: {}\".format(subject))\n", " req = crypto.X509Req()\n", " subj = req.get_subject()\n", " \n", " for i in ['C', 'ST', 'L', 'O', 'OU', 'CN']:\n", " if i in subject:\n", " setattr(subj, i, subject[i])\n", "\n", " req.set_pubkey(pkey)\n", " req.sign(pkey, digest)\n", " return req\n", "\n", "\n", "def create_priv_key_and_csr(cert_dir, csr_file, key_file, subject):\n", " if not exists(cert_dir):\n", " print(\"creating directory: {}\".format(cert_dir))\n", " makedirs(cert_dir)\n", " \n", " priv_key = crypto.PKey()\n", " priv_key.generate_key(crypto.TYPE_RSA, 2048)\n", " #print(crypto.dump_privatekey(crypto.FILETYPE_PEM, priv_key).decode('utf-8'))\n", "\n", " key_file = join(cert_dir, key_file)\n", " f = open(key_file,\"w\")\n", " f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, priv_key).decode('utf-8'))\n", " f.close()\n", " \n", " csr = createCertRequest(priv_key, subject)\n", "\n", " csr_file = join(cert_dir, csr_file)\n", " f= open(csr_file,\"w\")\n", " f.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, csr).decode('utf-8'))\n", " f.close()\n", " \n", " return crypto.dump_certificate_request(crypto.FILETYPE_PEM, csr)\n", "\n", "\n", "def request_cert_from_pca(subject):\n", " device_key_file = '{}.device.key.pem'.format(subject['CN'])\n", " device_csr_file = '{}.device.csr.pem'.format(subject['CN'])\n", " device_cert_file = '{}.device.cert.pem'.format(subject['CN'])\n", "\n", " device_csr = create_priv_key_and_csr(config['PCA_directory'], \n", " device_csr_file, \n", " device_key_file, \n", " subject)\n", " print(\"device_csr: {}\".format(device_csr))\n", "\n", " idempotency_token = '{}_id_token'.format(subject['CN'])\n", " response = c_acm_pca.issue_certificate(\n", " CertificateAuthorityArn = pca_arn,\n", " Csr = device_csr,\n", " SigningAlgorithm = 'SHA256WITHRSA',\n", " Validity= {\n", " 'Value': 365,\n", " 'Type': 'DAYS'\n", " },\n", " IdempotencyToken = idempotency_token\n", " )\n", "\n", " print(\"response: {}\".format(response))\n", "\n", " certificate_arn = response['CertificateArn']\n", " print(\"certificate_arn: {}\".format(certificate_arn))\n", " \n", " waiter = c_acm_pca.get_waiter('certificate_issued')\n", " try:\n", " waiter.wait(\n", " CertificateAuthorityArn=pca_arn,\n", " CertificateArn=certificate_arn\n", " )\n", " except botocore.exceptions.WaiterError as e:\n", " raise Exception(\"waiter: {}\".format(e))\n", " \n", " response = c_acm_pca.get_certificate(\n", " CertificateAuthorityArn = pca_arn,\n", " CertificateArn = certificate_arn\n", " )\n", " print(\"response: {}\".format(response))\n", " device_cert = response['Certificate']\n", " print(\"device_cert: {}\".format(device_cert))\n", "\n", " file_device_crt = join(config['PCA_directory'], device_cert_file)\n", " f = open(file_device_crt,\"w\")\n", " f.write(device_cert)\n", " f.write(\"\\n\")\n", " f.write(pca_certificate)\n", " f.close()\n", " \n", " print(\"device_key_file: {}\".format(device_key_file))\n", " print(\"device_csr_file: {}\".format(device_csr_file))\n", " print(\"device_cert_file: {}\".format(device_cert_file))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Boto3 Client" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c_acm_pca = boto3.client('acm-pca', region_name = config['aws_region_pca'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PCA Certificate and ARN\n", "Get the CA certificate for the private CA as well as it's arn. The arn is required to issue certificates and for the private CA certificate will be stored together with the device certificate." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = c_acm_pca.list_certificate_authorities(MaxResults=50)\n", "\n", "for ca in response['CertificateAuthorities']:\n", " if ca['CertificateAuthorityConfiguration']['Subject']['CommonName'] == config['Sub_CN']:\n", " pca_arn = ca['Arn']\n", " break\n", "\n", "print(\"pca_arn: {}\".format(pca_arn))\n", "\n", "response = c_acm_pca.get_certificate_authority_certificate(\n", " CertificateAuthorityArn = pca_arn\n", ")\n", "#print(\"response: {}\".format(json.dumps(response, indent=4, default=str)))\n", "pca_certificate = response['Certificate']\n", "print(\"pca_certificate:\\n{}\".format(pca_certificate))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Request Device Certificate\n", "The CN in the CSR must be set to reflect the thing name. When using a Lambda function for Just-in-Time registration the Lambda will get the thing name from the device certificate.\n", "\n", "Feel free to change the thing name as you like." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "request_cert_from_pca({\"CN\": \"thing-mr04\"})" ] } ], "metadata": { "kernelspec": { "display_name": "conda_python3", "language": "python", "name": "conda_python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 4 }