From 3e196a37bd19e01bd332cb1a5586856496738150 Mon Sep 17 00:00:00 2001 From: jzonthemtn Date: Mon, 25 Nov 2024 13:02:51 -0500 Subject: [PATCH] Experimenting with signing. --- distribution/example-filter.sh | 2 +- distribution/policies/default.json | 9 -- .../philter/api/WebMvcConfiguration.java | 16 ++++ .../api/controllers/ExplainApiController.java | 2 +- .../api/controllers/FilterApiController.java | 28 +++++- .../api/model/SignedFilterResponse.java | 18 ++++ .../test/philter/api/SignTextTest.java | 94 +++++++++++++++++++ pom.xml | 2 +- 8 files changed, 154 insertions(+), 17 deletions(-) create mode 100644 philter-api/src/main/java/ai/philterd/philter/api/WebMvcConfiguration.java create mode 100644 philter-api/src/main/java/ai/philterd/philter/api/model/SignedFilterResponse.java create mode 100644 philter-api/src/test/java/ai/philterd/test/philter/api/SignTextTest.java diff --git a/distribution/example-filter.sh b/distribution/example-filter.sh index f6edffe..16bad41 100755 --- a/distribution/example-filter.sh +++ b/distribution/example-filter.sh @@ -1,3 +1,3 @@ #!/bin/bash -curl -k "https://localhost:8080/api/filter" \ +curl "http://localhost:8080/api/filter" \ --data "George Washington was president and his ssn was 123-45-6789 and he lived in 90210." -H "Content-type: text/plain" diff --git a/distribution/policies/default.json b/distribution/policies/default.json index 102a584..62b1f72 100644 --- a/distribution/policies/default.json +++ b/distribution/policies/default.json @@ -10,15 +10,6 @@ "ignored": [], "identifiers": { "dictionaries": [], - "person": { - "phEyeConfiguration": { - "endpoint": "http://philter-ph-eye-1:5000" - }, - "personFilterStrategies": [{ - "strategy": "REDACT", - "redactionFormat": "{{{REDACTED-%t}}}" - }] - }, "age": { "ageFilterStrategies": [{ "strategy": "REDACT", diff --git a/philter-api/src/main/java/ai/philterd/philter/api/WebMvcConfiguration.java b/philter-api/src/main/java/ai/philterd/philter/api/WebMvcConfiguration.java new file mode 100644 index 0000000..50adbdd --- /dev/null +++ b/philter-api/src/main/java/ai/philterd/philter/api/WebMvcConfiguration.java @@ -0,0 +1,16 @@ +package ai.philterd.philter.api; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfiguration implements WebMvcConfigurer { + + @Override + public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + configurer.defaultContentType(MediaType.TEXT_PLAIN); + } + +} \ No newline at end of file diff --git a/philter-api/src/main/java/ai/philterd/philter/api/controllers/ExplainApiController.java b/philter-api/src/main/java/ai/philterd/philter/api/controllers/ExplainApiController.java index b17c581..b0cfdb1 100644 --- a/philter-api/src/main/java/ai/philterd/philter/api/controllers/ExplainApiController.java +++ b/philter-api/src/main/java/ai/philterd/philter/api/controllers/ExplainApiController.java @@ -37,7 +37,7 @@ public ExplainApiController(FilterService filterService, Gson gson) { final FilterResponse response = filterService.filter(policies, context, documentId, body, MimeType.TEXT_PLAIN); return ResponseEntity.status(HttpStatus.OK) - .header("x-document-id", response.documentId()) + .header("x-document-id", response.getDocumentId()) .contentType(MediaType.APPLICATION_JSON) .body(gson.toJson(response)); diff --git a/philter-api/src/main/java/ai/philterd/philter/api/controllers/FilterApiController.java b/philter-api/src/main/java/ai/philterd/philter/api/controllers/FilterApiController.java index 48b3e42..bd0a529 100644 --- a/philter-api/src/main/java/ai/philterd/philter/api/controllers/FilterApiController.java +++ b/philter-api/src/main/java/ai/philterd/philter/api/controllers/FilterApiController.java @@ -1,9 +1,11 @@ package ai.philterd.philter.api.controllers; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.logging.Filter; +import ai.philterd.philter.api.model.SignedFilterResponse; import ai.philterd.philter.services.PhilterService; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -45,7 +47,7 @@ public FilterApiController(FilterService filterService) { LOGGER.info("Received uploaded binary PDF file to be returned as ZIP."); - final List policies = Arrays.asList(policyName); + final List policies = Collections.singletonList(policyName); final BinaryDocumentFilterResponse response = filterService.filter(policies, context, documentId, body, MimeType.APPLICATION_PDF, MimeType.IMAGE_JPEG); return ResponseEntity.status(HttpStatus.OK) @@ -63,7 +65,7 @@ public FilterApiController(FilterService filterService) { LOGGER.info("Received uploaded binary PDF file to be returned as PDF."); - final List policies = Arrays.asList(policyName); + final List policies = Collections.singletonList(policyName); final BinaryDocumentFilterResponse response = filterService.filter(policies, context, documentId, body, MimeType.APPLICATION_PDF, MimeType.APPLICATION_PDF); return ResponseEntity.status(HttpStatus.OK) @@ -72,6 +74,22 @@ public FilterApiController(FilterService filterService) { } + @RequestMapping(value="/api/filter", method=RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.TEXT_PLAIN_VALUE) + public @ResponseBody ResponseEntity filterTextPlainAsJson( + @RequestParam(value="c", defaultValue="none") String context, + @RequestParam(value="d", defaultValue="") String documentId, + @RequestParam(value="p", defaultValue="default") String policyName, + @RequestBody String body) throws Exception { + + final List policies = Collections.singletonList(policyName); + final FilterResponse response = filterService.filter(policies, context, documentId, body, MimeType.TEXT_PLAIN); + + return ResponseEntity.status(HttpStatus.OK) + .header("x-document-id", response.getDocumentId()) + .body(response); + + } + @RequestMapping(value="/api/filter", method=RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.TEXT_PLAIN_VALUE) public @ResponseBody ResponseEntity filterTextPlainAsTextPlain( @RequestParam(value="c", defaultValue="none") String context, @@ -79,12 +97,12 @@ public FilterApiController(FilterService filterService) { @RequestParam(value="p", defaultValue="default") String policyName, @RequestBody String body) throws Exception { - final List policies = Arrays.asList(policyName); + final List policies = Collections.singletonList(policyName); final FilterResponse response = filterService.filter(policies, context, documentId, body, MimeType.TEXT_PLAIN); return ResponseEntity.status(HttpStatus.OK) - .header("x-document-id", response.documentId()) - .body(response.filteredText()); + .header("x-document-id", response.getDocumentId()) + .body(response.getFilteredText()); } diff --git a/philter-api/src/main/java/ai/philterd/philter/api/model/SignedFilterResponse.java b/philter-api/src/main/java/ai/philterd/philter/api/model/SignedFilterResponse.java new file mode 100644 index 0000000..5e82475 --- /dev/null +++ b/philter-api/src/main/java/ai/philterd/philter/api/model/SignedFilterResponse.java @@ -0,0 +1,18 @@ +package ai.philterd.philter.api.model; + +import ai.philterd.phileas.model.responses.FilterResponse; + +public class SignedFilterResponse extends FilterResponse { + + private final String signature; + + public SignedFilterResponse(final FilterResponse filterResponse, String signature) { + super(filterResponse.getFilteredText(), filterResponse.getContext(), filterResponse.getDocumentId(), filterResponse.getPiece(), filterResponse.getExplanation(), filterResponse.getAttributes()); + this.signature = signature; + } + + public String getSignature() { + return signature; + } + +} diff --git a/philter-api/src/test/java/ai/philterd/test/philter/api/SignTextTest.java b/philter-api/src/test/java/ai/philterd/test/philter/api/SignTextTest.java new file mode 100644 index 0000000..9c9749b --- /dev/null +++ b/philter-api/src/test/java/ai/philterd/test/philter/api/SignTextTest.java @@ -0,0 +1,94 @@ +package ai.philterd.test.philter.api; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.cert.Certificate; +import java.util.Base64; + +public class SignTextTest { + + @Test + public void test() throws Exception { + + // Have to create the private/public keys. + // Have to sign the text and send back the signature + + // Load private key for signing: + // keytool -genkeypair -alias senderKeyPair -keyalg RSA -keysize 2048 -dname "CN=Baeldung" -validity 365 -storetype JKS -keystore sender_keystore.jks -storepass changeit + + // Export public key: + // keytool -exportcert -alias senderKeyPair -storetype JKS -keystore sender_keystore.jks -file sender_certificate.cer -rfc -storepass changeit + + final KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(new FileInputStream("/tmp/sender_keystore.jks"), "changeit".toCharArray()); + + final PrivateKey privateKey = (PrivateKey) keyStore.getKey("senderKeyPair", "changeit".toCharArray()); + + // Create empty key store on the receiver side. + // keytool -genkeypair -alias receiverKeyPair -keyalg RSA -keysize 2048 -dname "CN=Baeldung" -validity 365 -storetype JKS -keystore receiver_keystore.jks -storepass changeit + // keytool -delete -alias receiverKeyPair -storepass changeit -keystore receiver_keystore.jks + + // Import the public key into the empty key store. + // keytool -importcert -alias receiverKeyPair -storetype JKS -keystore receiver_keystore.jks -file sender_certificate.cer -rfc -storepass changeit + + // Load the public key + + final KeyStore keyStore2 = KeyStore.getInstance("JKS"); + keyStore2.load(new FileInputStream("/tmp/receiver_keystore.jks"), "changeit".toCharArray()); + final Certificate certificate = keyStore2.getCertificate("receiverKeyPair"); + final PublicKey publicKey = certificate.getPublicKey(); + + //final String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded()); + //System.out.println(encodedPublicKey); + + // --------------------------- + + final Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(privateKey); + + final byte[] messageBytes = "hello world".getBytes(); + + signature.update(messageBytes); + final byte[] digitalSignature = signature.sign(); + + // Convert the signature to a string to be returned. + final String encodedDigitalSignature = Base64.getEncoder().encodeToString(digitalSignature); + //System.out.println(encodedDigitalSignature); + + boolean valid = verify(encodedDigitalSignature, "hello world"); + + Assert.assertTrue(valid); + + } + + public boolean verify(final String digitalSignature, final String redactedText) throws Exception { + + // Load the public key + + final KeyStore keyStore2 = KeyStore.getInstance("JKS"); + keyStore2.load(new FileInputStream("/tmp/receiver_keystore.jks"), "changeit".toCharArray()); + + final Certificate certificate = keyStore2.getCertificate("receiverKeyPair"); + final PublicKey publicKey = certificate.getPublicKey(); + + final String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded()); + //System.out.println(encodedPublicKey); + + final Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(publicKey); + + byte[] messageBytes = "hello world".getBytes(); + + signature.update(messageBytes); + + return signature.verify(Base64.getDecoder().decode(digitalSignature)); + + } + +} diff --git a/pom.xml b/pom.xml index 80220fc..24f00e8 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ 4.13.2 1.13.3 1.10.19 - 2.9.1 + 2.10.0-SNAPSHOT 1.4.0 3.3.1 6.1.10