HMAC Auth SDK for Java
SafeSky HMAC Authentication SDK for Java
Java SDK for authenticating requests to the SafeSky API using HMAC-SHA256 signatures.
Click here to download the SDK
API Endpoint: All requests use
https://uav-api.safesky.app. Each API key is associated with either a sandbox or production environment. The gateway automatically routes your request to the correct backend based on the environment configured for your API key.
Installation
No external dependencies required for the core library! Uses only Java standard library.
Requirements:
- Java 17+ (for text blocks and modern APIs)
<!-- For the example file only (optional), add Gson for JSON handling -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
Quick Start
import java.util.Map;
public class QuickStart {
public static void main(String[] args) {
// Your SafeSky API key
String apiKey = "YOUR_SAFESKY_API_KEY_HERE";
// Generate authentication headers
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(
apiKey,
"GET",
"https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908",
null // body is null for GET requests
);
// Use with your HTTP client
// headers contains: Authorization, X-SS-Date, X-SS-Nonce, X-SS-Alg
}
}
Usage Examples
Example 1: GET Request (Nearby Aircraft) with HttpURLConnection
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Map;
String apiKey = "YOUR_SAFESKY_API_KEY_HERE";
String url = "https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000";
// Generate headers
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "GET", url, null);
// Make request
HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection();
conn.setRequestMethod("GET");
// Add auth headers
for (Map.Entry<String, String> header : headers.entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
int statusCode = conn.getResponseCode();
// Read response...
conn.disconnect();
Example 2: POST Request (Publish UAV Position)
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.Gson;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
String apiKey = "YOUR_SAFESKY_API_KEY_HERE";
String url = "https://uav-api.safesky.app/v1/uav";
// Build JSON body
JsonArray bodyArray = new JsonArray();
JsonObject uav = new JsonObject();
uav.addProperty("id", "my_uav_001");
uav.addProperty("altitude", 110);
uav.addProperty("latitude", 50.69378);
uav.addProperty("longitude", 4.39201);
uav.addProperty("last_update", System.currentTimeMillis() / 1000);
uav.addProperty("status", "AIRBORNE");
uav.addProperty("call_sign", "Test UAV");
uav.addProperty("ground_speed", 10);
uav.addProperty("course", 250);
uav.addProperty("vertical_rate", 5);
bodyArray.add(uav);
String body = new Gson().toJson(bodyArray);
// Generate headers (pass body for POST)
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "POST", url, body);
// Make request
HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
for (Map.Entry<String, String> header : headers.entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
// Write body
try (OutputStream os = conn.getOutputStream()) {
byte[] input = body.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
int statusCode = conn.getResponseCode();
conn.disconnect();
Complete Examples
The SDK includes 5 complete examples in SafeSkyHmacExample.java:
- GET nearby aircraft - Retrieve traffic in a specific area
- POST UAV position - Submit UAV telemetry data
- POST UAV with query parameters - Submit UAV and get nearby traffic
- POST advisory (GeoJSON) - Publish GeoJSON FeatureCollection with polygon and point
- Manual step-by-step - Detailed authentication flow
Example 3: POST with Query Parameters
// URL includes query parameters
String url = "https://uav-api.safesky.app/v1/uav?return_nearby_traffic=true";
String body = /* JSON body */;
// Query params are automatically included in signature
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "POST", url, body);
Example 4: POST Advisory (GeoJSON)
String url = "https://uav-api.safesky.app/v1/advisory";
long timestamp = System.currentTimeMillis() / 1000;
String body = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"id": "advisory_001",
"max_altitude": 111,
"last_update": %d,
"call_sign": "Test Advisory",
"remarks": "Testing"
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[4.38, 50.69],
[4.40, 50.69],
[4.40, 50.70],
[4.38, 50.70],
[4.38, 50.69]
]]
}
}
]
}
""".formatted(timestamp);
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "POST", url, body);
Example 5: Using with Apache HttpClient
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.util.Map;
String apiKey = "YOUR_SAFESKY_API_KEY_HERE";
String url = "https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908";
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "GET", url, null);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet(url);
// Add auth headers
for (Map.Entry<String, String> header : headers.entrySet()) {
request.addHeader(header.getKey(), header.getValue());
}
httpClient.execute(request);
}
Example 6: Using with OkHttp
import okhttp3.*;
import java.util.Map;
String apiKey = "YOUR_SAFESKY_API_KEY_HERE";
String url = "https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908";
Map<String, String> headers = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "GET", url, null);
OkHttpClient client = new OkHttpClient();
Request.Builder requestBuilder = new Request.Builder()
.url(url)
.get();
// Add auth headers
for (Map.Entry<String, String> header : headers.entrySet()) {
requestBuilder.addHeader(header.getKey(), header.getValue());
}
Response response = client.newCall(requestBuilder.build()).execute();
Example 7: Using with Spring RestTemplate
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
String apiKey = "YOUR_SAFESKY_API_KEY_HERE";
String url = "https://uav-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908";
Map<String, String> authHeaders = SafeSkyHmacAuth.generateAuthHeaders(apiKey, "GET", url, null);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// Add auth headers
authHeaders.forEach(headers::add);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
API Reference
generateAuthHeaders(apiKey, method, url, body) → Map<String, String>
Main function - Generates all HMAC authentication headers.
Parameters:
apiKey(String): Your SafeSky API key (e.g.,ssk_live_...)method(String): HTTP method (GET,POST, etc.)url(String): Full URL including protocol, host, path, and query stringbody(String): Request body for POST/PUT requests,nullfor GET requests
Returns: Map with authentication headers:
{
"Authorization": "SS-HMAC Credential=...",
"X-SS-Date": "2025-11-12T14:30:00.123Z",
"X-SS-Nonce": "uuid-v4",
"X-SS-Alg": "SS-HMAC-SHA256-V1"
}
Lower-Level Functions
For advanced use cases, you can use individual static methods:
deriveKid(String apiKey)→String- Derives Key Identifier from API keyderiveHmacKey(String apiKey)→byte[]- Derives HMAC signing key using HKDFbuildCanonicalRequest(...)→String- Builds canonical request stringgenerateSignature(String canonicalRequest, byte[] hmacKey)→String- Generates HMAC-SHA256 signaturegenerateNonce()→String- Generates UUID v4 noncegenerateTimestamp()→String- Generates ISO8601 timestamp with milliseconds
Authentication Flow
- Derive KID:
KID = base64url(SHA256("kid:" + api_key)[0:16]) - Derive HMAC Key: HKDF-SHA256 with salt and info strings
- Generate Nonce: UUID v4 for replay protection
- Generate Timestamp: ISO8601 format with milliseconds
- Build Canonical Request:
METHOD /path query_string host:hostname:port x-ss-date:timestamp x-ss-nonce:nonce body_hash_sha256_hex - Sign:
HMAC-SHA256(hmac_key, canonical_request)→ base64 - Build Headers: Authorization header with KID, signed headers, and signature
Compiling and Running
Compile the library:
javac SafeSkyHmacAuth.java
Compile and run the example (requires Gson):
## Download Gson jar
wget https://repo1.maven.org/maven2/com/google/code/gson/gson/2.10.1/gson-2.10.1.jar
## Compile
javac -cp gson-2.10.1.jar SafeSkyHmacAuth.java SafeSkyHmacExample.java
## Run
java -cp .:gson-2.10.1.jar SafeSkyHmacExample
Using with Maven:
Add to your pom.xml:
<dependencies>
<!-- Optional: Only needed if using Gson for JSON handling -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
Security Notes
- Keep API keys secret: Never commit them to version control
- Use environment variables: Store keys in secure configuration
- HTTPS only: Always use HTTPS in production (api.safesky.app)
- Replay protection: Server validates nonces within 15-minute window
- Clock skew: Server allows ±5 minutes timestamp tolerance
Troubleshooting
Invalid Signature
Check:
- Body is identical (no extra whitespace, encoding issues)
- URL includes all query parameters
- Host header matches URL (include port for non-standard ports)
Replay Attack Detected
- Nonce was already used within 15-minute window
- Generate a fresh nonce for each request
Timestamp Out of Range
- Server time differs by more than ±5 minutes
- Synchronize your system clock with NTP
Authentication Failed
- Verify API key is correct (starts with
ssk_live_orssk_test_) - Check API key has required permissions for the endpoint
- Ensure all headers are present
Running Examples
## Make sure server is running on localhost:8080 for local testing
## or update API_ENDPOINT to https://uav-api.safesky.app for production
java -cp .:gson-2.10.1.jar SafeSkyHmacExample
Support
For questions or issues:
- Email: support@safesky.app
- Documentation: https://docs.safesky.app