HttpSignature.java
package org.linkedopenactors.rdfpub.adapter.driven;
import java.io.IOException;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.tomitribe.auth.signatures.SigningAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class HttpSignature {
private MessageDigest messageDigest;
public HttpSignature() {
try {
messageDigest = MessageDigest.getInstance("SHA256");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("", e);
}
}
// public String signature(String keyId, PrivateKey privateKey, String uriString, HttpMethod method) {
// URI uri = URI.create(uriString);
// final Map<String, String> headers = new HashMap<String, String>();
// headers.put("Host", uri.getAuthority());
// headers.put("Date", getCurrentDate());
//
// List<String> headerKeys = List.of("(request-target)", "host", "date");
//
// final org.tomitribe.auth.signatures.Signature signature = new org.tomitribe.auth.signatures.Signature(keyId, SigningAlgorithm.HS2019.getAlgorithmName(), "RSA_SHA256", null, null, headerKeys);
//
// final Key key = new SecretKeySpec(privateKey.getEncoded(), "HmacSHA256");
//
// final org.tomitribe.auth.signatures.Signer signer = new org.tomitribe.auth.signatures.Signer(key, signature);
//
// try {
// final org.tomitribe.auth.signatures.Signature signed = signer.sign(method.toString(), uri.getPath(), headers);
// return signed.toString();
// } catch (IOException e) {
// throw new IllegalStateException("unable to generate http signature.", e);
// }
// }
// public SignatureResponse test(String keyId, PrivateKey privateKey, String uriString, HttpMethod method, String payload) {
// try {
// URI uri = URI.create(uriString);
//
// final Map<String, String> headers = new HashMap<>();
// headers.put("host", uri.getAuthority());
// headers.put("date", getCurrentDate());
// headers.put("digest", getMessageDigest(payload));
//
// String toBeSigned = "(request-target): post " + uri.getPath();
// toBeSigned += "\nhost: " + uri.getAuthority();
// toBeSigned += "\ndate: " + getCurrentDate();
// toBeSigned += "\ndigest: " + getMessageDigest(payload);
//
// log.debug("toBeSigned: " + toBeSigned);
//
// MessageDigest md = MessageDigest.getInstance("SHA256");
// byte[] messageHash = md.digest(toBeSigned.getBytes());
//
// Signature sig = Signature.getInstance( "SHA256withRSA" );
// sig.initSign(privateKey);
// sig.update(messageHash);
// byte[] encryptedMessageHash = sig.sign();
//
// String signature = Base64.getEncoder().encodeToString(encryptedMessageHash); // TODO encoder as instance var ?
// String sigStr = "keyId=\"" + keyId + "\",headers=\"(request-target) host date digest\",signature=\"" + signature + "\"";
//
// headers.put("signature", sigStr);
// SignatureResponse response = new SignatureResponse(headers/*, signature*/);
// return response;
// } catch (Exception e) {
// throw new IllegalStateException("error signing", e);
// }
// }
public SignatureResponse signature(String keyId, PrivateKey privateKey, String uriString, HttpMethod method, String payload) {
URI uri = URI.create(uriString);
final Map<String, String> headers = new HashMap<>();
headers.put("host", uri.getAuthority());
headers.put("date", getCurrentDate());
headers.put("digest", getMessageDigest(payload));
try {
List<String> headerKeys = List.of("(request-target)", "Host", "Date", "Digest");
final org.tomitribe.auth.signatures.Signature signatureTemplate = new org.tomitribe.auth.signatures.Signature(keyId, SigningAlgorithm.RSA_SHA256.getAlgorithmName(), "RSA_SHA256", null, null, headerKeys);
final org.tomitribe.auth.signatures.Signer signer = new org.tomitribe.auth.signatures.Signer(privateKey, signatureTemplate);
final org.tomitribe.auth.signatures.Signature signature = signer.sign(method.toString(), uri.getPath(), headers);
String signatureHeaderValue = signature.toString().substring("Signature ".length());
headers.put("signature", signatureHeaderValue);
SignatureResponse response = new SignatureResponse(headers/*, signature*/);
return response;
} catch (IOException e) {
throw new IllegalStateException("unable to generate http signature.", e);
}
}
private String getMessageDigest(String payload) {
String digest;
try {
byte[] messageHash = messageDigest.digest(payload.getBytes());
digest = Base64.getEncoder().encodeToString(messageHash); // TODO encoder as instance var ?
digest = "sha-256=" + digest;
log.trace("payload: " + payload);
log.debug("digest: " + digest);
} catch (Exception e) {
throw new IllegalStateException("unable to generate digest.", e);
}
return digest;
}
private String getCurrentDate() {
Locale locale = Locale.forLanguageTag( "en-EN" );
DateTimeFormatter df = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z")
.withLocale(locale)
.withZone(ZoneId.of("GMT"));
String date = LocalDateTime.now(ZoneId.of("GMT")).format(df);
return date;
}
@Data
public class SignatureResponse {
private String host;
private String date;
private String digest;
private String signatureString;
public SignatureResponse(final Map<String, String> headers) {
setHost(Optional.ofNullable(headers.get("host")).orElseThrow());
setDate(Optional.ofNullable(headers.get("date")).orElseThrow());
setDigest(Optional.ofNullable(headers.get("digest")).orElseThrow());
setSignatureString(Optional.ofNullable(headers.get("signature")).orElseThrow());
}
}
public String getMessageHash(String payloadDigest) {
byte[] messageHash;
try {
messageHash = messageDigest.digest(payloadDigest.getBytes());
return Base64.getEncoder().encodeToString(messageHash); // TODO encoder as instance var ?
} catch (Exception e) {
throw new IllegalStateException("unable to generate digest.", e);
}
}
}