Skip to content
Snippets Groups Projects
Commit 1230bb59 authored by Martin Vogel's avatar Martin Vogel
Browse files

#414 Adds JWE crypto service

parent 25ecf659
No related branches found
No related tags found
2 merge requests!2#414 Remaining changes from MR,!1planning#414 Methoden Signaturen (Zwischenstand)
......@@ -16,4 +16,15 @@
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package fitconnect.api;
import com.nimbusds.jose.jwk.RSAKey;
import fitconnect.api.auth.OAuthToken;
import fitconnect.api.data.Data;
import fitconnect.api.data.Metadata;
import fitconnect.api.validation.ValidationResult;
import java.io.ByteArrayInputStream;
/**
* A technical system that creates a submission via the Submission API.
......@@ -22,31 +18,19 @@ public interface Sender {
*/
OAuthToken retrieveAuthenticationToken(final String clientId, final String clientSecret, String... scope);
// TODO
ValidationResult validateCertificateChain(byte[] chain);
/**
* Encrypts the submission data with JWE.
*
* @param unencryptedData - unencrypted JSON or XML data
* @return encrypted JSON or XML data
* @param unencryptedData - unencrypted JSON or XML data as string
* @return encrypted JSON or XML data as string
*/
Data encryptData(final Data unencryptedData);
String encryptData(RSAKey publicKey, String unencryptedData);
/**
* Encrypts the submission attachment with JWE.
* Encrypts a submission attachment with JWE.
*
* @param unencryptedAttachmentBinary - the unencrypted binary data of the attachment
* @return the encrypted binary of the attachment
* @param unencryptedAttachment - unencrypted bytes of the attachment
* @return encrypted JWE string serialization of the attachment
*/
ByteArrayInputStream encryptAttachment(final ByteArrayInputStream unencryptedAttachmentBinary);
/**
* Generates the metadata consisting of the attachment and its hash values.
*
* @param attachment - the unencrypted binary data of the attachment
* @return metadata object
*/
Metadata generateMetadata(byte[] attachment);
String encryptAttachment(RSAKey publicKey, byte[] unencryptedAttachment);
}
package fitconnect.client.impl;
import com.nimbusds.jose.jwk.RSAKey;
import fitconnect.api.Sender;
import fitconnect.api.auth.OAuthService;
import fitconnect.api.auth.OAuthToken;
import fitconnect.api.crypto.JWECryptoService;
public class SubmissionSender implements Sender {
private final OAuthService authService;
private final JWECryptoService cryptoService;
public SubmissionSender(final OAuthService authService, JWECryptoService cryptoService){
this.authService = authService;
this.cryptoService = cryptoService;
}
@Override
public OAuthToken retrieveAuthenticationToken(String clientId, String clientSecret, String... scope) {
return authService.authenticate(clientId, clientSecret, scope).orElseThrow();
}
@Override
public String encryptData(RSAKey publicKey, String unencryptedData) {
return cryptoService.encryptString(publicKey, unencryptedData);
}
@Override
public String encryptAttachment(RSAKey publicKey, byte[] unencryptedAttachment) {
return cryptoService.encryptBytes(publicKey, unencryptedAttachment);
}
}
package fitconnect.client.impl.crypto;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jose.jwk.RSAKey;
import fitconnect.api.crypto.JWECryptoService;
import fitconnect.api.problems.DecryptionProblem;
import fitconnect.api.problems.EncryptionProblem;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
public class FitCoCryptoService implements JWECryptoService {
public static final JWEAlgorithm ALGORITHM = JWEAlgorithm.RSA_OAEP_256;
public static final EncryptionMethod ENCRYPTION_METHOD = EncryptionMethod.A256GCM;
@Override
public String encryptString(RSAKey publicKey, String data) throws EncryptionProblem {
final Payload payload = getPayloadString(data);
return encrypt(publicKey, payload);
}
@Override
public String decryptString(RSAKey privateKey, String encryptedData) throws DecryptionProblem {
return decrypt(privateKey, encryptedData).toString();
}
@Override
public String encryptBytes(RSAKey publicKey, byte[] bytes) throws EncryptionProblem {
final Payload payload = getPayloadBytes(bytes);
return encrypt(publicKey, payload);
}
@Override
public byte[] decryptBytes(RSAKey privateKey, String encryptedData) throws DecryptionProblem {
return decrypt(privateKey, encryptedData).toBytes();
}
private String encrypt(RSAKey publicKey, Payload payload) throws EncryptionProblem {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(ENCRYPTION_METHOD.cekBitLength());
final SecretKey cek = keyGenerator.generateKey();
final String keyID = getIdFromPublicKey(publicKey);
return encryptPayload(publicKey,payload, cek, keyID);
} catch (NoSuchAlgorithmException | JOSEException e) {
throw new EncryptionProblem(e.getMessage());
}
}
private Payload decrypt(RSAKey privateKey, String encData) throws DecryptionProblem {
try {
final JWEObject jwe = JWEObject.parse(encData);
jwe.decrypt(new RSADecrypter(privateKey));
return jwe.getPayload();
} catch (ParseException | JOSEException e) {
throw new DecryptionProblem(e.getMessage());
}
}
private JWEObject getJWEObject(String keyID, Payload payload) {
return new JWEObject(getJWEHeader(keyID), payload);
}
private JWEHeader getJWEHeader(String keyID) {
return new JWEHeader.Builder(ALGORITHM, ENCRYPTION_METHOD)
.compressionAlgorithm(CompressionAlgorithm.DEF)
.contentType("application/json")
.keyID(keyID)
.build();
}
private RSAEncrypter getEncrypter(RSAPublicKey publicKey, SecretKey cek) {
return new RSAEncrypter(publicKey, cek);
}
private Payload getPayloadString(String data) {
return new Payload(data);
}
private Payload getPayloadBytes(byte[] bytes) {
return new Payload(bytes);
}
private String encryptPayload(RSAKey publicKey, Payload payload, SecretKey cek, String keyID) throws JOSEException {
JWEObject jwe = getJWEObject(keyID, payload);
jwe.encrypt(getEncrypter(publicKey.toRSAPublicKey(), cek));
checkIfJWEObjectIsEncrypted(jwe);
return jwe.serialize();
}
private void checkIfJWEObjectIsEncrypted(JWEObject jwe) {
if (!jwe.getState().equals(JWEObject.State.ENCRYPTED)) {
throw new EncryptionProblem("JWE object is not encrypted");
}
}
private String getIdFromPublicKey(RSAKey publicKey) {
final String keyID = publicKey.getKeyID();
if (keyID == null || keyID.isEmpty()) {
throw new EncryptionProblem("public key has no keyID");
}
return keyID;
}
}
package fitconnect.client.crypto;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import fitconnect.client.impl.crypto.FitCoCryptoService;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
class JWECryptoServiceTest {
final fitconnect.api.crypto.JWECryptoService underTest = new FitCoCryptoService();
@Test
void encryptData() throws JOSEException {
// Given
final RSAKey privateKey = generateRsaKey(4096);
final RSAKey publicKey = privateKey.toPublicJWK();
// When
String encryptedData = underTest.encryptString(publicKey, "some foo");
// Then
assertNotNull(encryptedData);
assertNotEquals("some foo", encryptedData);
}
@Test
void decryptData() throws JOSEException {
// Given
final RSAKey privateKey = generateRsaKey(4096);
final RSAKey publicKey = privateKey.toPublicJWK();
String encryptedData = underTest.encryptString(publicKey, "some foo");
// When
String decrypted = underTest.decryptString(privateKey, encryptedData);
// Then
assertNotNull(decrypted);
assertEquals("some foo", decrypted);
}
public static RSAKey generateRsaKey(int size) throws JOSEException {
return new RSAKeyGenerator(size)
.keyUse(KeyUse.ENCRYPTION)
.keyID(UUID.randomUUID().toString())
.generate();
}
}
\ No newline at end of file
......@@ -51,6 +51,17 @@
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.19</version>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem</artifactId>
<version>0.27.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
......@@ -60,4 +71,18 @@
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment