Skip to content

Commit

Permalink
Retrieving pgp keys from s3 (#493)
Browse files Browse the repository at this point in the history
* Updated deploy configs for staging
  • Loading branch information
sree-cfa authored Jan 5, 2024
1 parent 6a12961 commit b44e371
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 159 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/deploy-staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,17 @@ jobs:
'AWS_SECRET_KEY=${{ secrets.AWS_SECRET_KEY_STAGING }}' \
'AWS_BUCKET=${{ secrets.AWS_BUCKET_STAGING }}' \
'AWS_CMK=${{ secrets.AWS_CMK_STAGING }}' \
'PGP_KEYS_DIR'=${{ secrets.PGP_KEYS_DIR }}' \
'PGP_SECKEY_FILE_PATH'=${{ secrets.PGP_SECKEY_FILE_PATH_STAGING }}' \
'PGP_PUBKEY_FILE_PATH'=${{ secrets.PGP_PUBKEY_FILE_PATH_STAGING }}' \
'PGP_SIGKEY_PASSWORD'=${{ secrets.SIGKEY_PASSWORD_STAGING }}' \
'FTPS_USERNAME'=${{ secrets.FTPS_USERNAME }}
'FTPS_PASSWORD'=${{ secrets.FTPS_PASSWORD }}
'FTPS_UPLOAD_URL'=${{ secrets.FTPS_UPLOAD_URL }}
'FTPS_UPLOAD_DIR'=${{ secrets.FTPS_UPLOAD_DIR_STAGING }}
'SENTRY_DSN=${{ secrets.SENTRY_DSN }}' \
'SENTRY_ENVIRONMENT=staging' \
'FORCE_SSL=true' \
'ENCRYPTION_KEY=${{ secrets.ENCRYPTION_KEY }}' \
'SPRING_DATASOURCE_USERNAME=${{ secrets.SPRING_DATASOURCE_USERNAME_STAGING }}' \
'SPRING_DATASOURCE_PASSWORD=${{ secrets.SPRING_DATASOURCE_PASSWORD_STAGING }}' \
'SFTP_USERNAME=${{ secrets.SFTP_USERNAME_STAGING }}' \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@Slf4j
@Component
@Profile("production")
@Profile({"production", "staging"})
public class FtpsClientImpl implements FtpsClient {

private final String username;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

@Slf4j
@Component
@Profile("!production")
@Profile({"dev", "test", "demo"})
public class MockFtpsClientImpl implements FtpsClient {

@Override
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/org/ladocuploader/app/cli/MockPGPEncryptorImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.ladocuploader.app.cli;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
@Component
@Profile({"dev", "test", "demo"})
public class MockPGPEncryptorImpl implements PGPEncryptor {

@Override
public byte[] signAndEncryptPayload(String filename) throws IOException {
log.info("Mock encrypting payload " + filename);
try (InputStream inputStream = new FileInputStream(filename)) {
return inputStream.readAllBytes();
}
}
}
152 changes: 3 additions & 149 deletions src/main/java/org/ladocuploader/app/cli/PGPEncryptor.java
Original file line number Diff line number Diff line change
@@ -1,154 +1,8 @@
package org.ladocuploader.app.cli;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;

import java.io.*;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Iterator;

@Slf4j
@Component
public class PGPEncryptor {

private final String pubkeyFilename;
private final String sigkeyFilename;
private final String sigkeyPassword;

public PGPEncryptor(@Value("${pgp.pubkey_filename:}") String pubkeyFilename, @Value("${pgp.sigkey_filename:}") String sigkeyFilename, @Value("${pgp.sigkey_password:}") String sigkeyPassword) {
this.pubkeyFilename = pubkeyFilename;
this.sigkeyFilename = sigkeyFilename;
this.sigkeyPassword = sigkeyPassword;
}

public byte[] signAndEncryptPayload(String filename) throws IOException {
FileInputStream instream = new FileInputStream(filename);

log.info("Retrieving keys for signing and encryption");
PGPSecretKey signingKey = getSecretKey();
PGPPublicKey pubKey = getPublicKey();

ByteArrayOutputStream outstream = new ByteArrayOutputStream();
try {
log.info("Signing and encrypting payload");
return signAndEncryptPayload(instream, signingKey, pubKey, outstream);
} catch (PGPException e) {
throw new IllegalStateException("There was an issue signing and encrypting the file", e);
} finally {
instream.close();
outstream.close();
log.info("Completed signing and encrypting payload");
}
}

private PGPPublicKey getPublicKey() throws IOException {
PGPPublicKey pubKey = null;
InputStream inputStream = new FileInputStream(pubkeyFilename);
inputStream = PGPUtil.getDecoderStream(inputStream);
try {
JcaPGPPublicKeyRingCollection ringCollection = new JcaPGPPublicKeyRingCollection(inputStream);
Iterator<PGPPublicKeyRing> keyRingsIterator = ringCollection.getKeyRings();
while (keyRingsIterator.hasNext()) {
PGPPublicKeyRing pgpPublicKeyRing = keyRingsIterator.next();
Iterator<PGPPublicKey> pubKeysIterator = pgpPublicKeyRing.getPublicKeys();
while (pubKeysIterator.hasNext()) {
pubKey = pubKeysIterator.next();
}
}
} catch (PGPException e) {
throw new IllegalArgumentException("Invalid public key");
} finally {
inputStream.close();
}
return pubKey;
}

private PGPSecretKey getSecretKey() throws IOException {
try (InputStream fileInputStream = new FileInputStream(sigkeyFilename);) {
InputStream inputStream = PGPUtil.getDecoderStream(fileInputStream);
KeyFingerPrintCalculator fpCalculator = new JcaKeyFingerprintCalculator();
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(inputStream, fpCalculator);

Iterator<PGPSecretKeyRing> keyRingIter = pgpSec.getKeyRings();
while (keyRingIter.hasNext()) {
PGPSecretKeyRing keyRing = keyRingIter.next();

Iterator<PGPSecretKey> keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext()) {
PGPSecretKey key = keyIter.next();

if (key.isSigningKey()) {
return key;
}
}
}
} catch (PGPException e) {
throw new IllegalArgumentException("Invalid signing key", e);
}

throw new IllegalArgumentException("Invalid signing key");
}

private PGPPrivateKey getPrivateKey(PGPSecretKey secretKey) throws PGPException {
return secretKey.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().build(sigkeyPassword.toCharArray()));
}

private byte[] signAndEncryptPayload(InputStream inputStream, PGPSecretKey secKey, PGPPublicKey pubKey,
ByteArrayOutputStream outputStream) throws PGPException, IOException {
int BUFFER_SIZE = 1 << 16;

// Encryption
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom()));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(pubKey));
OutputStream encOut = encGen.open(outputStream, new byte[BUFFER_SIZE]);

// Compression
PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
OutputStream cOut = cGen.open(encOut);

// Signing
PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(secKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1));
sGen.init(PGPSignature.BINARY_DOCUMENT, getPrivateKey(secKey));

Iterator<String> it = secKey.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.addSignerUserID(false, it.next());
sGen.setHashedSubpackets(spGen.generate());
}

sGen.generateOnePassVersion(false).encode(cOut);

// Literal Data generator and output stream
byte[] data = inputStream.readAllBytes();
PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
OutputStream lOut = lGen.open(cOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, data.length, new Date());

lOut.write(data);
sGen.update(data);

lOut.close();
lGen.close();

sGen.generate().encode(cOut);
cOut.close();
cGen.close();
encGen.close();

return outputStream.toByteArray();
}
public interface PGPEncryptor {

byte[] signAndEncryptPayload(String filename) throws IOException;
}

179 changes: 179 additions & 0 deletions src/main/java/org/ladocuploader/app/cli/PGPEncryptorImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package org.ladocuploader.app.cli;

import formflow.library.file.CloudFile;
import formflow.library.file.S3CloudFileRepository;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import java.io.*;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Iterator;

@Slf4j
@Component
@Profile({"production", "staging"})
public class PGPEncryptorImpl implements PGPEncryptor {

@Value("${pgp.sigkey-password}")
private String sigkeyPassword;
@Value("${pgp.seckey-file-path}")
private String seckeyFilePath;
@Value("${pgp.pubkey-file-path}")
private String pubkeyFilePath;
@Value("${pgp.bucket-name}")
private String bucketName;

@Value("${form-flow.aws.access_key}")
private String accessKey;
@Value("${form-flow.aws.secret_key}")
private String secretKey;
@Value("${form-flow.aws.region}")
private String region;

private PGPSecretKey signingKey;
private PGPPublicKey pubKey;

@PostConstruct
public void init() {
log.info("Retrieving keys for signing and encryption");
S3CloudFileRepository repository = new S3CloudFileRepository(accessKey, secretKey, bucketName, region);
CloudFile pubKey = repository.get(pubkeyFilePath);
CloudFile sigKey = repository.get(seckeyFilePath);
try {
initPubKey(pubKey.getFileBytes());
initSigKey(sigKey.getFileBytes());
} catch (IOException e) {
throw new IllegalStateException("Issue initializing encryption keys", e);
}
}

@Override
public byte[] signAndEncryptPayload(String filename) throws IOException {
FileInputStream instream = new FileInputStream(filename);
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
try {
log.info("Signing and encrypting payload");
return signAndEncryptPayload(instream, signingKey, pubKey, outstream);
} catch (PGPException e) {
throw new IllegalStateException("There was an issue signing and encrypting the file", e);
} finally {
instream.close();
outstream.close();
log.info("Completed signing and encrypting payload");
}
}

private void initPubKey(byte[] fileBytes) throws IOException {
PGPPublicKey pubKey = null;
InputStream inputStream = new ByteArrayInputStream(fileBytes);
inputStream = PGPUtil.getDecoderStream(inputStream);
try {
JcaPGPPublicKeyRingCollection ringCollection = new JcaPGPPublicKeyRingCollection(inputStream);
Iterator<PGPPublicKeyRing> keyRingsIterator = ringCollection.getKeyRings();
while (keyRingsIterator.hasNext()) {
PGPPublicKeyRing pgpPublicKeyRing = keyRingsIterator.next();
Iterator<PGPPublicKey> pubKeysIterator = pgpPublicKeyRing.getPublicKeys();
while (pubKeysIterator.hasNext()) {
pubKey = pubKeysIterator.next();
}
}
} catch (PGPException e) {
throw new IllegalArgumentException("Invalid public key");
} finally {
inputStream.close();
}
this.pubKey = pubKey;
}

private void initSigKey(byte[] sigkeyFileBytes) throws IOException {
try (InputStream fileInputStream = new ByteArrayInputStream(sigkeyFileBytes);) {
InputStream inputStream = PGPUtil.getDecoderStream(fileInputStream);
KeyFingerPrintCalculator fpCalculator = new JcaKeyFingerprintCalculator();
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(inputStream, fpCalculator);

Iterator<PGPSecretKeyRing> keyRingIter = pgpSec.getKeyRings();
while (keyRingIter.hasNext()) {
PGPSecretKeyRing keyRing = keyRingIter.next();

Iterator<PGPSecretKey> keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext()) {
PGPSecretKey key = keyIter.next();

if (key.isSigningKey()) {
signingKey = key;
return;
}
}
}
} catch (PGPException e) {
throw new IllegalArgumentException("Invalid signing key", e);
}

throw new IllegalArgumentException("Invalid signing key");
}

private PGPPrivateKey getPrivateKey(PGPSecretKey secretKey) throws PGPException {
return secretKey.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().build(sigkeyPassword.toCharArray()));
}

private byte[] signAndEncryptPayload(InputStream inputStream, PGPSecretKey secKey, PGPPublicKey pubKey,
ByteArrayOutputStream outputStream) throws PGPException, IOException {
int BUFFER_SIZE = 1 << 16;

// Encryption
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom()));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(pubKey));
OutputStream encOut = encGen.open(outputStream, new byte[BUFFER_SIZE]);

// Compression
PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
OutputStream cOut = cGen.open(encOut);

// Signing
PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(secKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1));
sGen.init(PGPSignature.BINARY_DOCUMENT, getPrivateKey(secKey));

Iterator<String> it = secKey.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.addSignerUserID(false, it.next());
sGen.setHashedSubpackets(spGen.generate());
}

sGen.generateOnePassVersion(false).encode(cOut);

// Literal Data generator and output stream
byte[] data = inputStream.readAllBytes();
PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
OutputStream lOut = lGen.open(cOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, data.length, new Date());

lOut.write(data);
sGen.update(data);

lOut.close();
lGen.close();

sGen.generate().encode(cOut);
cOut.close();
cGen.close();
encGen.close();

return outputStream.toByteArray();
}

}

Loading

0 comments on commit b44e371

Please sign in to comment.