Fortinet black logo

API Guide

HMAC Authentication

Copy Link
Copy Doc ID e6bafec5-1d0e-11ec-8c53-00505692583a:797122
Download PDF

HMAC Authentication

To get public/private key, you need to create the appliance in the system and the copy keys from that system. For more information, see the Appliances section in the Security Management chapter of the "Administration Guide."

How FortiSOAR Authorizes Payloads with HMAC

To successfully authenticate our payloads, we are required to get a fingerprint as part of the request and compare it locally by recreating the fingerprint. This is subjective and can be defined by the HMAC provider (FortiSOAR). FortiSOAR fingerprinting will be based on hashing the private key against the identifier (NOT THE ACTUAL PAYLOAD).

How to create an Identifier

The FortiSOAR identifier is created in such a way that some pieces will be gathered from the request and others will have to be provided by the requester. Our current identifier simply is: ALGO.VERB.TIMESTAMP.FULL_URI.HASHED_PAYLOAD

The identifier units are separated with periods. Here is a brief explanation of each unit:

  • ALGO: The Algorithm the hashing is being done with, for a full list of hashing algorithms available in PHP you can use the hash_algos function.
  • VERB: The current request action, GET, POST, PUT, PATCH, DELETE
  • TIMESTAMP: The timestamp the fingerprint is created. This is also sent as part of the request.
    Note: Timestamp must be in UTC format.
  • FULL_URI: The full URI of the request, including any parameters
  • HASHED_PAYLOAD: The string that is to be fingerprinted. Hashed with the requested ALGO

Once the above is created the fingerprint is created with hash_hmac(algo, identifier, private_key, false). The fingerprint is expected to be represented as a string as opposed to raw binary.

PHP Example

$privateKey = "our_private_key";
$publicKey = "our_public_key"; // used in the example following this

  $testAlgo = "sha256";
     $testVerb = "GET";
     $testTimestampObj = new \DateTime();
     $testTimestamp = $testTimestampObj->format('Y-m-d H:i:s');
     $testFullUri = "http://example.com/api/1/data/1";

    $testPayload = "This is a test string"; // this could be

our_public_key on GET requests
    $hashedPayload = hash($testAlgo,$testPayload,false);

    $rawFingerprint =
         $testAlgo.'.'.
         $testVerb.'.'.
         $testTimestamp.'.'.
         $testFullUri.'.'.
         $hashedPayload;

    $hashedFingerprint =
         hash_hmac(
             $testAlgo,
             $rawFingerprint,
             $privateKey,
             false
         );

Python Example

import base64
import hashlib
import hmac
from datetime import datetime

DEFAULT_ALGORITHM = "sha256"
with open("APPLIANCE_PUBLIC_KEY", 'r') as public_key_file:
   public_key = public_key_file.read().strip()
with open("APPLIANCE_PRIVATE_KEY", 'r') as private_key_file:
   private_key = private_key_file.read().strip()
   
def generate_hmac(method, full_uri, payload, private_key, public_key):
    if method == 'GET':
        payload = public_key
    timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
    payload = payload if type(payload) is bytes else payload.encode()
    digest_method = hashlib.new(DEFAULT_ALGORITHM)
    digest_method.update(payload)
    hashed_payload = digest_method.hexdigest()
    raw_fingerprint = "{0}.{1}.{2}.{3}.{4}".format(DEFAULT_ALGORITHM,
                                                   method,
                                                   timestamp,
                                                   full_uri,
                                                   hashed_payload)

    hashed = hmac.new(private_key.encode(), raw_fingerprint.encode(),
                      hashlib.sha256)
    hashed_fingerprint = hashed.hexdigest()
    header = base64.b64encode(
        '{0};{1};{2};{3}'.format(DEFAULT_ALGORITHM, timestamp, public_key,
                                 hashed_fingerprint).encode())
    return 'CS {}'.format(header.decode())

Using the Signature

After creating our identifier, we can send the payload to our API. However, the fingerprint alone is not enough. We'll also need to send our public key, the timestamp used to hash our fingerprint, and the algorithm being used in the request. These pieces should be concatenated using semicolons and included in the authorization header base64 encoded.

PHP Example

$header =
base64_encode($testAlgo.';'.$testTimestamp.';'.$publicKey.';'.$hashedFinge
rprint);

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
 "Authorization: CS " . $header,
 "X-CS-Data: " . $testPayload
));

Python Example

import requests  
import json
auth_header = generate_hmac(method,full_url, json.dumps(payload), private_key, public_key)
headers = 
{
  'Authorization': auth_header
}
try:
  req = requests.request(method, full_url, data=payload, headers=headers, verify=False)
  print (req)
except Exception as e:
  print(e)

If the fingerprint is authorized the request will be delivered to the API; otherwise, a 401 error will be returned.

HMAC Authentication

To get public/private key, you need to create the appliance in the system and the copy keys from that system. For more information, see the Appliances section in the Security Management chapter of the "Administration Guide."

How FortiSOAR Authorizes Payloads with HMAC

To successfully authenticate our payloads, we are required to get a fingerprint as part of the request and compare it locally by recreating the fingerprint. This is subjective and can be defined by the HMAC provider (FortiSOAR). FortiSOAR fingerprinting will be based on hashing the private key against the identifier (NOT THE ACTUAL PAYLOAD).

How to create an Identifier

The FortiSOAR identifier is created in such a way that some pieces will be gathered from the request and others will have to be provided by the requester. Our current identifier simply is: ALGO.VERB.TIMESTAMP.FULL_URI.HASHED_PAYLOAD

The identifier units are separated with periods. Here is a brief explanation of each unit:

  • ALGO: The Algorithm the hashing is being done with, for a full list of hashing algorithms available in PHP you can use the hash_algos function.
  • VERB: The current request action, GET, POST, PUT, PATCH, DELETE
  • TIMESTAMP: The timestamp the fingerprint is created. This is also sent as part of the request.
    Note: Timestamp must be in UTC format.
  • FULL_URI: The full URI of the request, including any parameters
  • HASHED_PAYLOAD: The string that is to be fingerprinted. Hashed with the requested ALGO

Once the above is created the fingerprint is created with hash_hmac(algo, identifier, private_key, false). The fingerprint is expected to be represented as a string as opposed to raw binary.

PHP Example

$privateKey = "our_private_key";
$publicKey = "our_public_key"; // used in the example following this

  $testAlgo = "sha256";
     $testVerb = "GET";
     $testTimestampObj = new \DateTime();
     $testTimestamp = $testTimestampObj->format('Y-m-d H:i:s');
     $testFullUri = "http://example.com/api/1/data/1";

    $testPayload = "This is a test string"; // this could be

our_public_key on GET requests
    $hashedPayload = hash($testAlgo,$testPayload,false);

    $rawFingerprint =
         $testAlgo.'.'.
         $testVerb.'.'.
         $testTimestamp.'.'.
         $testFullUri.'.'.
         $hashedPayload;

    $hashedFingerprint =
         hash_hmac(
             $testAlgo,
             $rawFingerprint,
             $privateKey,
             false
         );

Python Example

import base64
import hashlib
import hmac
from datetime import datetime

DEFAULT_ALGORITHM = "sha256"
with open("APPLIANCE_PUBLIC_KEY", 'r') as public_key_file:
   public_key = public_key_file.read().strip()
with open("APPLIANCE_PRIVATE_KEY", 'r') as private_key_file:
   private_key = private_key_file.read().strip()
   
def generate_hmac(method, full_uri, payload, private_key, public_key):
    if method == 'GET':
        payload = public_key
    timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
    payload = payload if type(payload) is bytes else payload.encode()
    digest_method = hashlib.new(DEFAULT_ALGORITHM)
    digest_method.update(payload)
    hashed_payload = digest_method.hexdigest()
    raw_fingerprint = "{0}.{1}.{2}.{3}.{4}".format(DEFAULT_ALGORITHM,
                                                   method,
                                                   timestamp,
                                                   full_uri,
                                                   hashed_payload)

    hashed = hmac.new(private_key.encode(), raw_fingerprint.encode(),
                      hashlib.sha256)
    hashed_fingerprint = hashed.hexdigest()
    header = base64.b64encode(
        '{0};{1};{2};{3}'.format(DEFAULT_ALGORITHM, timestamp, public_key,
                                 hashed_fingerprint).encode())
    return 'CS {}'.format(header.decode())

Using the Signature

After creating our identifier, we can send the payload to our API. However, the fingerprint alone is not enough. We'll also need to send our public key, the timestamp used to hash our fingerprint, and the algorithm being used in the request. These pieces should be concatenated using semicolons and included in the authorization header base64 encoded.

PHP Example

$header =
base64_encode($testAlgo.';'.$testTimestamp.';'.$publicKey.';'.$hashedFinge
rprint);

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
 "Authorization: CS " . $header,
 "X-CS-Data: " . $testPayload
));

Python Example

import requests  
import json
auth_header = generate_hmac(method,full_url, json.dumps(payload), private_key, public_key)
headers = 
{
  'Authorization': auth_header
}
try:
  req = requests.request(method, full_url, data=payload, headers=headers, verify=False)
  print (req)
except Exception as e:
  print(e)

If the fingerprint is authorized the request will be delivered to the API; otherwise, a 401 error will be returned.