aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/juick
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2020-11-10 16:36:02 +0300
committerGravatar Vitaly Takmazov2020-11-10 16:36:02 +0300
commit07ebf86ab279811c365e8174807dbf36fc2f4ca4 (patch)
tree9413da0d168bda99a8921eda9c97d4dd9341eb52 /src/main/java/com/juick
parenta0a70fd3c0b031426a34b4d62a7b3e11f1a90c64 (diff)
ActivityPub: Digest header is mandatory now for POST requests
Diffstat (limited to 'src/main/java/com/juick')
-rw-r--r--src/main/java/com/juick/ActivityPubManager.java11
-rw-r--r--src/main/java/com/juick/SignatureManager.java42
2 files changed, 38 insertions, 15 deletions
diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java
index e3b1ac8e..22fe826f 100644
--- a/src/main/java/com/juick/ActivityPubManager.java
+++ b/src/main/java/com/juick/ActivityPubManager.java
@@ -54,6 +54,7 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -104,7 +105,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
signatureManager.post(me, follower, accept);
socialService.addFollower(followedUser, follower.getId());
logger.info("Follower added for {}", followedUser.getName());
- } catch (IOException e) {
+ } catch (IOException | NoSuchAlgorithmException e) {
logger.info("activitypub exception", e);
}
}
@@ -145,7 +146,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
try {
logger.info("Deletion to follower {}", follower.getId());
signatureManager.post(me, follower, delete);
- } catch (IOException e) {
+ } catch (IOException | NoSuchAlgorithmException e) {
logger.warn("activitypub exception", e);
}
});
@@ -194,7 +195,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
try {
logger.info("Update to follower {}", follower.getId());
signatureManager.post(me, follower, update);
- } catch (IOException e) {
+ } catch (IOException | NoSuchAlgorithmException e) {
logger.warn("activitypub exception", e);
}
});
@@ -239,7 +240,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
create.setObject(note);
try {
signatureManager.post(me, follower, create);
- } catch (IOException e) {
+ } catch (IOException | NoSuchAlgorithmException e) {
logger.warn("activitypub exception", e);
}
}
@@ -386,7 +387,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
try {
logger.info("Announcing top: {}", message.getMid());
signatureManager.post(me, (Person)person, announce);
- } catch (IOException e) {
+ } catch (IOException | NoSuchAlgorithmException e) {
logger.warn("activitypub exception", e);
}
}, () -> logger.warn("Follower not found: {}", acct));
diff --git a/src/main/java/com/juick/SignatureManager.java b/src/main/java/com/juick/SignatureManager.java
index fc92f39a..fed6c368 100644
--- a/src/main/java/com/juick/SignatureManager.java
+++ b/src/main/java/com/juick/SignatureManager.java
@@ -26,6 +26,7 @@ import com.juick.www.api.activity.model.Context;
import com.juick.www.api.activity.model.objects.Person;
import com.juick.www.api.webfinger.model.Account;
import com.juick.www.api.webfinger.model.Link;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
@@ -34,6 +35,8 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
+import org.tomitribe.auth.signatures.Base64;
+import org.tomitribe.auth.signatures.MissingRequiredHeaderException;
import org.tomitribe.auth.signatures.Signature;
import org.tomitribe.auth.signatures.Signer;
import org.tomitribe.auth.signatures.Verifier;
@@ -43,10 +46,13 @@ import javax.inject.Inject;
import java.io.IOException;
import java.net.URI;
import java.security.Key;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.time.Instant;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -63,35 +69,51 @@ public class SignatureManager {
@Inject
private RestTemplate apClient;
- public void post(Person from, Person to, Context data) throws IOException {
+ public void post(Person from, Person to, Context data) throws IOException, NoSuchAlgorithmException {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(to.getInbox());
URI inbox = uriComponentsBuilder.build().toUri();
Instant now = Instant.now();
String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now);
String host = inbox.getPort() > 0 ? String.format("%s:%d", inbox.getHost(), inbox.getPort()) : inbox.getHost();
- String signatureString = addSignature(from, host, "POST", inbox.getPath(), requestDate);
+ var finalContext = Context.build(data);
+ var payload = jsonMapper.writeValueAsString(finalContext);
+ final byte[] digest = MessageDigest.getInstance("SHA-256").digest(payload.getBytes()); // (1)
+ final String digestHeader = "SHA-256=" + new String(Base64.encodeBase64(digest));
+ String signatureString = addSignature(from, host, "POST", inbox.getPath(), requestDate, digestHeader);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Content-Type", Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE);
requestHeaders.add("Date", requestDate);
requestHeaders.add("Host", host);
+ requestHeaders.add("Digest", digestHeader);
requestHeaders.add("Signature", signatureString);
- HttpEntity<Context> request = new HttpEntity<>(Context.build(data), requestHeaders);
- logger.info("Sending context to {}: {}", to.getId(), jsonMapper.writeValueAsString(data));
+ HttpEntity<Context> request = new HttpEntity<>(finalContext, requestHeaders);
+ logger.info("Sending context to {}: {}", to.getId(), payload);
ResponseEntity<Void> response = apClient.postForEntity(inbox, request, Void.class);
logger.info("Remote response: {}", response.getStatusCodeValue());
}
- public String addSignature(Person from, String host, String method, String path, String dateString) throws IOException {
- return addSignature(from, host, method, path, dateString, keystoreManager);
+ public String addSignature(Person from, String host, String method,
+ String path, String dateString, String digestHeader)
+ throws IOException {
+ return addSignature(from, host, method, path, dateString,
+ digestHeader, keystoreManager);
}
- public String addSignature(Person from, String host, String method, String path, String dateString, KeystoreManager keystoreManager) throws IOException {
- Signature templateSignature = new Signature(from.getPublicKey().getId(), "rsa-sha256", null,
- "(request-target)", "host", "date");
+ public String addSignature(Person from, String host, String method,
+ String path, String dateString, String digestHeader,
+ KeystoreManager keystoreManager) throws IOException {
+ List<String> requiredHeaders = StringUtils.isEmpty(digestHeader) ?
+ Arrays.asList("(request-target)", "host", "date")
+ : Arrays.asList("(request-target)", "host", "date", "digest");
+ Signature templateSignature = new Signature(from.getPublicKey().getId(),
+ "rsa-sha256", null, requiredHeaders);
Map<String, String> headers = new HashMap<>();
headers.put("host", host);
headers.put("date", dateString);
+ if (StringUtils.isNotEmpty(digestHeader)) {
+ headers.put("digest", digestHeader);
+ }
Signer signer = new Signer(keystoreManager.getPrivateKey(), templateSignature);
Signature signature = signer.sign(method, path, headers);
// remove "Signature: " from result
@@ -122,7 +144,7 @@ public class SignatureManager {
} else {
return AnonymousUser.INSTANCE;
}
- } catch (NoSuchAlgorithmException | SignatureException | IOException e) {
+ } catch (NoSuchAlgorithmException | SignatureException | MissingRequiredHeaderException | IOException e) {
logger.warn("Invalid signature {}", signatureString);
}
} else {