> For the complete documentation index, see [llms.txt](https://ajaib.gitbook.io/ajaib-exchange-open-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ajaib.gitbook.io/ajaib-exchange-open-api/getting-started/api-security.md).

# API Security

This document outlines the authentication process for accessing Ajaib's Open API. Ajaib utilize API Keys for authentication and Ed25519 signatures for securing your API calls.

### How to Generate Ed25519 Key Pair

To utilize Ajaib's Open API, you will need to generate an Ed25519 key pair. This pair consists of a private key and a public key. You can generate this key pair using the following methods:

#### Using OpenSSL

OpenSSL is a widely used command-line tool for cryptographic operations. You can generate an Ed25519 key pair with PEM encoding using the following command in your terminal:

```bash
openssl genpkey -algorithm Ed25519 -out private_key.pem
openssl pkey -in private_key.pem -pubout -out public_key.pem
```

This will create two files: `private_key.pem` and `public_key.pem`. You will need to keep your private key secure and use the public key for the next step.

#### Using Binance's Asymmetric Key Generator

Binance provides an open-source tool that can generate Ed25519 key pairs in the required PEM format. You can find the tool and its instructions on [their GitHub repository](https://github.com/binance/asymmetric-key-generator).

***

### Signing API Requests

All API requests must be signed with your Ed25519 private key. This signature proves the request is really from you and ensures it hasn’t been tampered with. Here’s how to do it properly:

1. **Prepare the request parameters**: Gather all request parameters as key-value pairs.
2. **Sort parameters**: Arrange them alphabetically by key. This ensures consistency in signature generation.
3. **Create the query string**: Concatenate sorted parameters into URL-encoded query string.
4. **Generate the signature**: Sign the query string using your Ed25519 private key.
5. **Base64-encode the signature**: Convert the signature into a base64 string.
6. **Attach the signature**: signature must be URL-encoded and appended as a parameter in the query string. Refer to this [page](https://ajaib.gitbook.io/ajaib-exchange-open-api/~/revisions/Ip9FY78nc19RqLechCXG/api-references/general-information) on how to insert the query string correctly in the request.

This is a sample code to show how to sign the payload with an Ed25519 key.

{% tabs %}
{% tab title="Python" %}

```python
#!/usr/bin/env python3

import base64
import requests
import time
from cryptography.hazmat.primitives.serialization import load_pem_private_key

# Set up authentication
API_KEY='<THIS_IS_THE_API_KEY>'
PRIVATE_KEY_PATH='private_key.pem'

# Load the private key.
# In this example the key is expected to be stored without encryption,
# but we recommend using a strong password for improved security.
with open(PRIVATE_KEY_PATH, 'rb') as f:
    private_key = load_pem_private_key(data=f.read(),
                                       password=None)

# Set up the request parameters
params = {
    'symbol':       'BTCUSDT',
    'side':         'SELL',
    'type':         'LIMIT',
    'timeInForce':  'GTC',
    'quantity':     '1.0000000',
    'price':        '0.20',
}

# Timestamp the request
timestamp = int(time.time_ns() // 1_000_000) # UNIX timestamp in milliseconds
params['timestamp'] = timestamp

# Sign the request
payload = '&'.join([f'{param}={value}' for param, value in params.items()])
signature = base64.b64encode(private_key.sign(payload.encode('ASCII')))
params['signature'] = signature

# Send the request
headers = {
    'X-MBX-APIKEY': API_KEY,
    'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.post(
    'https://api.example.com/v1/order',
    headers=headers,
    data=params,
)
print(response.json())

```

{% endtab %}

{% tab title="Golang" %}

```go
package main

import (
	"bytes"
	"crypto/ed25519"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"
)

func loadPrivateKey(path string) (ed25519.PrivateKey, error) {
	pemData, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("error reading PEM file: %v", err)
	}

	block, _ := pem.Decode(pemData)
	if block == nil {
		return nil, fmt.Errorf("failed to parse PEM block")
	}

	// Parse the private key. This example assumes a PKCS#8 encoded Ed25519 key.
	privItf, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse PKCS#8 private key: %v", err)
	}

	privKey, ok := privItf.(ed25519.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("not an Ed25519 private key")
	}

	return privKey, nil
}

func main() {
	// Set up authentication
	apiKey := "<THIS_IS_THE_API_KEY>"
	privateKeyPath := "private_key.pem"

	// Load private key.
	privKey, err := loadPrivateKey(privateKeyPath)
	if err != nil {
		log.Fatalf("Error loading private key: %v", err)
	}

	// Set up the request parameters
	params := map[string]string{
		"symbol":      "BTCUSDT",
		"side":        "SELL",
		"type":        "LIMIT",
		"timeInForce": "GTC",
		"quantity":    "1.0000000",
		"price":       "0.20",
	}

	// Timestamp the request
	timestamp := fmt.Sprintf("%d", time.Now().UnixMilli()) // UNIX timestamp in milliseconds
	params["timestamp"] = timestamp

	// Sign the request
	var buf bytes.Buffer
	for k, v := range params {
		if buf.Len() > 0 {
			buf.WriteByte('&')
		}
		buf.WriteString(fmt.Sprintf("%s=%s", k, v))
	}
	payload := buf.String()
	signature := base64.StdEncoding.EncodeToString(ed25519.Sign(privKey, []byte(payload)))
	params["signature"] = signature

	// Send the request
	form := url.Values{}
	for k, v := range params {
		form.Add(k, v)
	}
	req, err := http.NewRequest(http.MethodPost, "https://api.example.com/api/v1/order", bytes.NewBufferString(form.Encode()))
	if err != nil {
		log.Fatalf("Error creating request: %v", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("X-MBX-APIKEY", apiKey)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Error making POST request: %v", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Error reading response body: %v", err)
	}

	fmt.Println(string(respBody))
}

```

{% endtab %}
{% endtabs %}
