Java http signature sample code
This sample relies on the tomitribe's http-signatures-java.
Full project can be downloaded here
package io.knotcity;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.tomitribe.auth.signatures.Algorithm;
import org.tomitribe.auth.signatures.Join;
import org.tomitribe.auth.signatures.PEM;
import org.tomitribe.auth.signatures.Signature;
import org.tomitribe.auth.signatures.Signer;
import org.tomitribe.auth.signatures.SigningAlgorithm;
/**
* Hello world!
*/
public final class App {
private static final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build();
private final String BASE_ENDPOINT = "https://staging.staas.knotcity.io";
private final String privateKeyPem = "-----BEGIN PRIVATE KEY-----\n" +
"enter base64 byte of your key here\n" +
"-----END PRIVATE KEY-----";
private final String KEY_ID = "enter your key id here";
private App() {
}
/**
*
* @param verb get, post, delete, put, etc.
* @param urn the part of the url after the domain
* @param headers headers to sign, X-Api-Key and x-knot-date should always be signed.
* @throws InvalidKeySpecException
* @throws IOException
*/
private String signRequest(String verb, String urn, Map<String, String> headers) throws InvalidKeySpecException, IOException {
PrivateKey privateKey = PEM.readPrivateKey(new ByteArrayInputStream(privateKeyPem.getBytes()));
headers.put("(request-target)", verb + " " + urn);
Signer signer = new Signer(privateKey,
new Signature(KEY_ID, SigningAlgorithm.HS2019, Algorithm.ECDSA_SHA256, null, null, Arrays.asList(headers.keySet().toArray(new String[0]))));
Signature signed = signer.sign(verb, urn, headers);
return "Signature " +
"keyId=\"" + signed.getKeyId() + '\"' +
",algorithm=\"" + signed.getSigningAlgorithm() + '\"' +
",headers=\"" + Join.join(" ", signed.getHeaders()) + '\"' +
",signature=\"" + signed.getSignature() + '\"';
}
private String sendGet(String urn) throws Exception {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
headers.put("X-Api-Key", KEY_ID);
headers.put("x-knot-date", String.valueOf(ZonedDateTime.now().toInstant().toEpochMilli()));
List<String> headersList = new ArrayList<String>();
headers.forEach((key, value) -> {
headersList.add(key);
headersList.add(value);
});
headersList.add("Authorization");
headersList.add(this.signRequest("get", urn, headers));
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create(BASE_ENDPOINT + urn))
.headers(headersList.toArray(new String[0]))
.setHeader("User-Agent", "Java 11 HttpClient Bot")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// print response body
return response.body();
}
/**
* Says hello to the world.
* @param args The arguments of the program.
*/
public static void main(String[] args) {
try {
String body = new App().sendGet("/v1/enabled");
System.out.println(body);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}