0% found this document useful (0 votes)
25 views8 pages

KB CertificateManagementandCashDepositAPI 071124 1539

Uploaded by

gquispej04
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views8 pages

KB CertificateManagementandCashDepositAPI 071124 1539

Uploaded by

gquispej04
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

Certificate Management and Cash Deposit API

Overview

This documentation provides a detailed guide for the implementation of the Certificate Management and Cash Deposit API. The project is split
into two primary modules: CertificateManager for managing certificates and private keys, and CashDepositAPI for handling deposit
functionality to a secure API. This modular structure ensures reusable and maintainable code that can be adapted to different secure API
requirements.

1. Certificate Manager Module (certificate_manager.py)

Purpose
The CertificateManager module is designed to manage the creation, storage, and validation of RSA private keys and self-signed SSL
certificates. These certificates are used to authenticate API clients with the Cash Deposit API and other secure endpoints, ensuring secure
communication.

Key Features

RSA Key Generation: Generates a 2048-bit RSA private key for secure signing and encryption.
Self-Signed Certificate Creation: Creates an x.509 self-signed certificate, valid for 1 year, to authenticate against the API server.
File Management: Saves both the private key and certificate to specified file paths, ensuring they are accessible for secure API requests.
Reusable: Can be utilized by any other secure API requiring certificate-based authentication.

Code Walkthrough

1. Initialization: The CertificateManager class is initialized with file paths for the certificate and private key.
2. Key and Certificate Creation:
Generates an RSA private key with a public exponent of 65537 and key size of 2048 bits.
Saves the key to a PEM file for use with other APIs.
Creates a self-signed certificate with metadata like country, state, locality, organization, and common name.
3. Ensure Certificate and Key: Checks if the specified files exist, and if not, generates the required key and certificate.

Example Usage

from certificate_manager import CertificateManager

cert_path = "cert.pem"
key_path = "key.pem"
cert_manager = CertificateManager(cert_path, key_path)
cert_manager.ensure_cert_and_key()

This snippet initializes the CertificateManager and ensures the certificate and key are available.

2. Cash Deposit API Client (cash_deposit_api.py)

Purpose
The CashDepositAPI module handles interactions with the Cash Deposit API, specifically making secure deposit transactions. It uses the
certificate and private key generated by CertificateManager to authenticate the client.

Key Features

Secure API Requests: Uses the x.509 certificate and RSA key for secure communication with the deposit server.
Deposit Handling: Manages deposit transactions with the API, including details like the amount, currency, transaction number, and optional
fields like tenant ID and deposit date.
Error Handling: Captures and logs any request-related errors for troubleshooting.
Code Walkthrough

1. Deposit Data Class: The Deposit data class is used to represent the deposit request details, such as id, tenant, amount, currency,
and transaction number.
2. Deposit API Implementation:
The CashDepositAPI class is initialized with paths to the certificate and key files.
Constructs the JSON payload with mandatory and optional fields.
Sends a POST request to the deposit endpoint, securely attaching the certificate and private key.
Handles responses and potential errors.

Example Usage

from cash_deposit_api import CashDepositAPI, Deposit

cert_path = "cert.pem"
key_path = "key.pem"
deposit_api = CashDepositAPI(cert_path, key_path)

test_deposit = Deposit(
id="123",
tenant="ABC",
amount=20000,
currency="EUR",
trans_nr=4,
deposit_date="2013-12-24 20:00:00"
)

result = deposit_api.make_deposit(test_deposit)
if result.new_balance is not None:
print(f"Deposit successful! New balance: {result.new_balance} {result.
currency_code}")
else:
print("Deposit failed.")

This snippet initializes the CashDepositAPI and makes a deposit request using the provided deposit details.

3. Typical Workflow

1. Certificate Generation:
First, generate the certificate and key using CertificateManager.
This ensures that secure communication is established before interacting with any API.
2. Making a Deposit:
Use CashDepositAPI to initialize the deposit client.
Provide the necessary details for the deposit (e.g., amount, tenant, transaction number).
Handle the response, checking for errors or successful deposits.

4. Advantages of Modularization

Reusability: The CertificateManager is now decoupled from any specific API, allowing reuse for different secure API integrations.
Maintainability: Updates to the certificate generation process can be made in a single module, automatically benefiting all API clients.
Security: Centralized management of keys and certificates enhances security by ensuring a consistent approach to encryption and secure
communications.

5. Future Extensions
Extend CertificateManager: Add support for generating certificates with longer validity or certificates signed by an internal CA for production
environments.
Add Unit Tests: Implement unit tests for both CertificateManager and CashDepositAPI to ensure the modules are functioning
correctly under different scenarios.
Dockerize: Package the entire system in a Docker container for easy deployment across environments, ensuring consistency in how
certificates are managed and APIs are consumed.

6. File Structure

certificate_manager.py: Handles RSA key and certificate creation.


cash_deposit_api.py: Implements the deposit functionality.
Other Modules: Future APIs can also reuse CertificateManager to ensure consistent security practices.

# certificate_manager.py

import os
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.x509 import NameOID
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timedelta

class CertificateManager:
"""
Manages the creation of RSA keys and self-signed certificates.
This is crucial to ensure secure communications with APIs using x.509
certificates.
"""
def __init__(self, cert_path: str, key_path: str):
self.cert_path = cert_path
self.key_path = key_path

def create_key_and_cert(self):
"""Create an RSA private key and a self-signed certificate for
secure API requests."""

# Step 1: Generate an RSA Private Key


# - The private key will be used to sign the certificate and
future API requests.
private_key = rsa.generate_private_key(
public_exponent=65537, # A common value for the public
exponent
key_size=2048, # Key size of 2048 bits for security
purposes
backend=default_backend()
)

# Save the generated private key to a file for later use


with open(self.key_path, "wb") as key_file:
key_file.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM, # Save in PEM format
for compatibility
format=serialization.PrivateFormat.TraditionalOpenSSL, #
OpenSSL format for private key
encryption_algorithm=serialization.NoEncryption() # Save
without password for simplicity (only for development)
))

# Step 2: Generate a Self-Signed Certificate


# - The certificate will be used to authenticate against the API
server.
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
# Country (C)
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME,
"California"), # State (ST)
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
# Locality (L)
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Company"),
# Organization (O)
x509.NameAttribute(NameOID.COMMON_NAME, "mycompany.com"),
# Common Name (CN), typically domain name
])

# Define the certificate validity period (e.g., 1 year from the


current date)
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer # Since this is self-signed, issuer and subject are
the same
).public_key(
private_key.public_key() # Associate the public key with the
certificate
).serial_number(
x509.random_serial_number() # Generate a random unique serial
number for the certificate
).not_valid_before(
datetime.utcnow() # Certificate validity starts now
).not_valid_after(
datetime.utcnow() + timedelta(days=365) # Certificate expires
in one year
).add_extension(
x509.SubjectAlternativeName([x509.DNSName("localhost")]), #
Add Subject Alternative Name (SAN)
critical=False,
).sign(
private_key, hashes.SHA256(), default_backend() # Sign the
certificate using the private key and SHA-256 hash
)
# Save the generated certificate to a file for use with API
requests
with open(self.cert_path, "wb") as cert_file:
cert_file.write(cert.public_bytes(serialization.Encoding.PEM))

def ensure_cert_and_key(self):
"""Ensure that both certificate and key files exist, or create
them if they don't."""
if not os.path.exists(self.cert_path) or not os.path.exists(self.
key_path):
print("Creating new certificate and key...")
self.create_key_and_cert() # Create key and certificate if
they do not exist
print("Certificate and key created successfully.")
else:
print("Certificate and key already exist.")

# cash_deposit_api.py

import requests
from dataclasses import dataclass
from typing import Optional
from certificate_manager import CertificateManager # Import the
Certificate Manager

@dataclass
class Deposit:
# Represents a deposit request
id: str # Unique identifier to assign the deposit to a
driver/entity
tenant: Optional[str] # Optional tenant identifier (useful in multi-
tenant environments)
amount: int # Deposit amount in cents, e.g., 20000 for
€200.00
currency: str # Currency code, e.g., "EUR"
trans_nr: int # Unique transaction number for this deposit
deposit_date: Optional[str] = None # Optional: Date of the deposit (e.
g., "2013-12-24 20:00:00")

@dataclass
class DepositResponse:
# Represents a response to a deposit request
new_balance: Optional[int] = None # The new account balance after
the deposit
currency_code: Optional[str] = None # The currency code of the
balance (e.g., "EUR")
account_trans_nr: Optional[int] = None # The account transaction
number provided in the response
class CashDepositAPI:
"""
Handles the communication with the Cash Deposit API.
The API uses a secure x.509 certificate for authentication, which must
be supplied in each request.
"""
base_url = "https://fare-server:8443/deposit-payment" # Replace with
the actual server address
headers = {
"Content-Type": "application/vnd.de.ivu.fare.deposit.payment.
deposit.v1.0+json" # Media type for the API request
}

def __init__(self, cert_path: str, key_path: str):


self.cert_path = cert_path
self.key_path = key_path

def make_deposit(self, deposit: Deposit) -> DepositResponse:


"""
Send a deposit request to the Cash Deposit API and return the
response.

:param deposit: Deposit object containing deposit details.


:return: DepositResponse object containing the API response
details.
"""
url = f"{self.base_url}/deposit"

# Construct the request payload with all required and optional


fields
payload = {
"id": deposit.id, # Unique identifier for the driver/entity
"tenant": deposit.tenant, # Optional tenant ID for multi-
tenant systems
"amount": {
"value": deposit.amount, # Deposit amount in cents
"currencyCode": deposit.currency # Currency code, e.g.,
EUR
},
"transNr": deposit.trans_nr, # Unique transaction number
}
if deposit.deposit_date:
payload["depositDate"] = deposit.deposit_date # Optional
deposit date, in the format "YYYY-MM-DD HH:MM:SS"

try:
# Send the POST request to the deposit API endpoint
response = requests.post(
url, json=payload, headers=self.headers,
cert=(self.cert_path, self.key_path), verify=True #
Attach certificate and key for SSL verification
)
response.raise_for_status() # Raises an HTTPError if the
response code is 4xx or 5xx

# Parse the response JSON to extract relevant fields


response_data = response.json()
new_balance = response_data.get("newBalance", {}).get("value")
currency_code = response_data.get("newBalance", {}).get
("currencyCode")
account_trans_nr = response_data.get("accountTransNr")

# Return the parsed response wrapped in a DepositResponse


dataclass
return DepositResponse(
new_balance=new_balance,
currency_code=currency_code,
account_trans_nr=account_trans_nr
)

except requests.exceptions.RequestException as e:
# Log any request-related errors
print(f"Error making deposit: {e}")
return DepositResponse() # Return an empty response object on
failure

# Example usage with test data


if __name__ == "__main__":
# Paths to the generated certificate and private key files
cert_path = "cert.pem" # Update this to the desired file path for the
certificate
key_path = "key.pem" # Update this to the desired file path for the
private key

# Ensure that the certificate and private key are available for secure
communications
cert_manager = CertificateManager(cert_path, key_path)
cert_manager.ensure_cert_and_key()

# Initialize the CashDepositAPI class with the paths to the


certificate and key files
deposit_api = CashDepositAPI(cert_path, key_path)

# Create a test deposit object


test_deposit = Deposit(
id="123", # Unique ID to assign the deposit to a
specific driver/entity
tenant="ABC", # Optional tenant ID (useful in multi-
tenant systems)
amount=20000, # Amount in cents, e.g., 20000 for
€200.00
currency="EUR", # Currency code
trans_nr=4, # Unique transaction number for the
deposit
deposit_date="2013-12-24 20:00:00" # Optional deposit date
)

# Make the deposit request and print the result


result = deposit_api.make_deposit(test_deposit)
if result.new_balance is not None:
print(f"Deposit successful! New balance: {result.new_balance}
{result.currency_code}")
print(f"Transaction Number: {result.account_trans_nr}")
else:
print("Deposit failed.")

You might also like