Secure PHP APIs with Signature Verification Using OpenSSL
Tags: php, programming, tutorial, security
Published: 2023-09-15 08:21:37

Application Programming Interfaces (APIs) are essential in web development. To ensure the security of API requests, it is often necessary to verify the authenticity of these requests. This article will discuss how to design a set of API signature verification programs in PHP, and provide specific design steps and code samples.

## Overview: Signature Verification Process With OpenSSL

The API signature verification process is a way to ensure that API requests have not been tampered with or come from an unauthorized source. It works by using a digital signature to bind the request data to the identity of the sender.

The signature verification process typically includes the following steps:

1. The client signs the request data by encrypting it with their private key. The signature is included in the request parameters.
2. The server extracts the signature from the request parameters.
3. The server reproduces the signature by encrypting the request data with the same private key and algorithm.
4. The server compares the original and reproduced signatures. If they match, the request is authenticated. Otherwise, verification fails.

## Implementation Guide

This section provides a step-by-step guide to implementing API signature verification with OpenSSL.

### Generate Key Pair

First, generate a pair of public and private keys for encryption and decryption. You can use the openssl extension to generate a key pair. The specific code is as follows:

```php
// Configuration for key generation
$config = array(
  "private_key_bits" => 2048, // Key size: 2048 bits 
  "private_key_type" => OPENSSL_KEYTYPE_RSA, // RSA algorithm
);

// Generate new private and public keys 
$res = openssl_pkey_new($config);

// Extract the generated private key 
openssl_pkey_export($res, $private_key); 

// Extract the public key component
$public_key = openssl_pkey_get_details($res);
$public_key = $public_key["key"]; 

// Save keys to files
file_put_contents('private.key', $private_key);
file_put_contents('public.key', $public_key);
```
The above code will generate a private key and save it to `private.key` file, and save the public key to a `public.key` file.

## Implementing Signature Functions

This section covers implementing the core signature functions.

### Loading Keys

Before signature generation and verification, you need to load the private and public keys. You can create a `Signature` class and load the private and public keys in the constructor like this:

```php
class Signature {
    private $private_key;
    private $public_key;

    public function __construct() {
        $this->private_key = openssl_pkey_get_private(file_get_contents('private.key'));
        $this->public_key = openssl_pkey_get_public(file_get_contents('public.key'));
    }

    // Other methods and code will be introduced later
}
```
### Generate Signature

To generate a signature, you need a method that accepts the request parameters and returns the signature. You can use OpenSSL's `openssl_sign()` function:

```php
public function generateSignature($params) {

  // Convert the request parameters array to a string.
  $data = http_build_query($params);

  // Sign the data with the private key.
  openssl_sign($data, $signature, $this->private_key);

  // Encode the signature result in Base64.
  $signature = base64_encode($signature);

  // Return the signature.
  return $signature;

}
```

### Verify Signature

```php
public function verifySignature($params, $clientSignature) {

  // Convert request params array to string
  $data = http_build_query($params);

  // Base64 decode client's signature
  $clientSignature = base64_decode($clientSignature);

  // Verify signature using public key
  $result = openssl_verify($data, $clientSignature, $this->public_key);

  // Return true if signatures match
  return $result === 1;

}
```

## Use an SDK to call APIs

To make it easier for clients to call an API and perform signature verification, you can create a simple SDK. The SDK will provide encapsulated methods that you can use to easily call APIs and perform signature verification.

Here is the code for a simple SDK:

```php
class APIClient {

  private $apiURL;
  private $signature;

  public function __construct($apiURL, $privateKeyPath, $publicKeyPath) {

    $this->apiURL = $apiURL;

    // Initialize signature validator
    $this->signature = new Signature($privateKeyPath, $publicKeyPath);

  }

  public function callAPI($params) {

    // Generate signature
    $signature = $this->signature->generateSignature($params);

    // Add signature to request parameters
    $params['signature'] = $signature;

    // Send API request
    $response = $this->sendRequest($params);

    // Verify signature
    $isValid = $this->signature->verifySignature($params, $response['signature']);

    // If verification succeeds, return API response data
    if ($isValid) {
      return $response['data'];
    } else {
      throw new Exception("Invalid signature");
    }

  }

  private function sendRequest($params) {

    // Use cURL library to send HTTP request
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $this->apiURL);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);

    // Parse API response data
    return json_decode($response, true);

  }

}
```

### SDK usage example:

```php

$apiURL = 'http://xyz.com/api.php';
$privateKeyPath = '/path/to/private.key'; 
$publicKeyPath = '/path/to/public.key';

// Create APIClient instance  
$client = new APIClient($apiURL, $privateKeyPath, $publicKeyPath);

// Prepare API request parameters
$params = array(
  'param1' => 'value1',
  'param2' => 'value2', 
);

try {

  // Call API and get response  
  $result = $client->callAPI($params);

  // Handle API response data
  // ...

} catch (Exception $e) {

  // Handle exception
  // ...

}
```

## Conclusion

This article demonstrated implementing API signature verification in PHP by generating a key pair with OpenSSL, creating signature functions with openssl_sign and openssl_verify, and building an SDK to simplify usage. Applying these cryptographic techniques secures API communications by allowing clients to digitally sign requests which can be validated by the server, preventing tampering and ensuring integrity.

The signature binding provides non-repudiation and protects against unauthorized access. While further enhancements are possible, the methods covered provide a solid foundation for applying signatures to safeguard APIs.