mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-26 06:12:06 +01:00
Wip: SOP 4
This commit is contained in:
parent
9cdea63ec4
commit
9a545a2936
16 changed files with 429 additions and 58 deletions
|
@ -200,7 +200,7 @@ public final class KeyRingTemplates {
|
||||||
* @throws PGPException in case of an OpenPGP related error
|
* @throws PGPException in case of an OpenPGP related error
|
||||||
*/
|
*/
|
||||||
public PGPSecretKeyRing modernKeyRing(String userId) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
public PGPSecretKeyRing modernKeyRing(String userId) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
return modernKeyRing(userId, null);
|
return modernKeyRing(userId, (Passphrase) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -217,13 +217,19 @@ public final class KeyRingTemplates {
|
||||||
*/
|
*/
|
||||||
public PGPSecretKeyRing modernKeyRing(String userId, String password)
|
public PGPSecretKeyRing modernKeyRing(String userId, String password)
|
||||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
||||||
|
Passphrase passphrase = (password != null ? Passphrase.fromPassword(password) : null);
|
||||||
|
return modernKeyRing(userId, passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPSecretKeyRing modernKeyRing(String userId, Passphrase passphrase)
|
||||||
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
KeyRingBuilder builder = PGPainless.buildKeyRing()
|
KeyRingBuilder builder = PGPainless.buildKeyRing()
|
||||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
||||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS))
|
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS))
|
||||||
.addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA))
|
.addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA))
|
||||||
.addUserId(userId);
|
.addUserId(userId);
|
||||||
if (!isNullOrEmpty(password)) {
|
if (passphrase != null && !passphrase.isEmpty()) {
|
||||||
builder.setPassphrase(Passphrase.fromPassword(password));
|
builder.setPassphrase(passphrase);
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,11 @@ group 'org.pgpainless'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'org.jetbrains:annotations:20.1.0'
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,12 @@ package org.pgpainless.sop;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
|
@ -23,9 +26,8 @@ import org.pgpainless.decryption_verification.ConsumerOptions;
|
||||||
import org.pgpainless.decryption_verification.DecryptionStream;
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
import org.pgpainless.exception.MissingDecryptionMethodException;
|
import org.pgpainless.exception.MissingDecryptionMethodException;
|
||||||
|
import org.pgpainless.exception.WrongPassphraseException;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
import sop.DecryptionResult;
|
import sop.DecryptionResult;
|
||||||
import sop.ReadyWithResult;
|
import sop.ReadyWithResult;
|
||||||
|
@ -37,6 +39,8 @@ import sop.operation.Decrypt;
|
||||||
public class DecryptImpl implements Decrypt {
|
public class DecryptImpl implements Decrypt {
|
||||||
|
|
||||||
private final ConsumerOptions consumerOptions = new ConsumerOptions();
|
private final ConsumerOptions consumerOptions = new ConsumerOptions();
|
||||||
|
private final Set<PGPSecretKeyRing> keys = new HashSet<>();
|
||||||
|
private final MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptImpl verifyNotBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
public DecryptImpl verifyNotBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
@ -96,29 +100,34 @@ public class DecryptImpl implements Decrypt {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptImpl withKey(InputStream keyIn) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo {
|
public DecryptImpl withKey(InputStream keyIn) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo {
|
||||||
try {
|
try {
|
||||||
PGPSecretKeyRingCollection secretKeys = PGPainless.readKeyRing()
|
PGPSecretKeyRingCollection secretKeyCollection = PGPainless.readKeyRing()
|
||||||
.secretKeyRingCollection(keyIn);
|
.secretKeyRingCollection(keyIn);
|
||||||
|
for (PGPSecretKeyRing key : secretKeyCollection) {
|
||||||
for (PGPSecretKeyRing secretKey : secretKeys) {
|
keys.add(key);
|
||||||
KeyRingInfo info = new KeyRingInfo(secretKey);
|
|
||||||
if (!info.isFullyDecrypted()) {
|
|
||||||
throw new SOPGPException.KeyIsProtected();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
consumerOptions.addDecryptionKeys(secretKeys, SecretKeyRingProtector.unprotectedKeys());
|
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt withKeyPassword(byte[] password) {
|
||||||
|
String string = new String(password, Charset.forName("UTF8"));
|
||||||
|
protector.addPassphrase(Passphrase.fromPassword(string));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadyWithResult<DecryptionResult> ciphertext(InputStream ciphertext)
|
public ReadyWithResult<DecryptionResult> ciphertext(InputStream ciphertext)
|
||||||
throws SOPGPException.BadData,
|
throws SOPGPException.BadData,
|
||||||
SOPGPException.MissingArg {
|
SOPGPException.MissingArg {
|
||||||
|
for (PGPSecretKeyRing key : keys) {
|
||||||
|
protector.addSecretKey(key);
|
||||||
|
consumerOptions.addDecryptionKey(key, protector);
|
||||||
|
}
|
||||||
|
|
||||||
if (consumerOptions.getDecryptionKeys().isEmpty() && consumerOptions.getDecryptionPassphrases().isEmpty() && consumerOptions.getSessionKey() == null) {
|
if (consumerOptions.getDecryptionKeys().isEmpty() && consumerOptions.getDecryptionPassphrases().isEmpty() && consumerOptions.getSessionKey() == null) {
|
||||||
throw new SOPGPException.MissingArg("Missing decryption key, passphrase or session key.");
|
throw new SOPGPException.MissingArg("Missing decryption key, passphrase or session key.");
|
||||||
|
@ -131,8 +140,12 @@ public class DecryptImpl implements Decrypt {
|
||||||
.withOptions(consumerOptions);
|
.withOptions(consumerOptions);
|
||||||
} catch (MissingDecryptionMethodException e) {
|
} catch (MissingDecryptionMethodException e) {
|
||||||
throw new SOPGPException.CannotDecrypt();
|
throw new SOPGPException.CannotDecrypt();
|
||||||
|
} catch (WrongPassphraseException e) {
|
||||||
|
throw new SOPGPException.KeyIsProtected();
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
|
} finally {
|
||||||
|
protector.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ReadyWithResult<DecryptionResult>() {
|
return new ReadyWithResult<DecryptionResult>() {
|
||||||
|
|
|
@ -32,28 +32,28 @@ import sop.ReadyWithResult;
|
||||||
import sop.SigningResult;
|
import sop.SigningResult;
|
||||||
import sop.enums.SignAs;
|
import sop.enums.SignAs;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.Sign;
|
import sop.operation.DetachedSign;
|
||||||
|
|
||||||
public class SignImpl implements Sign {
|
public class DetachedSignImpl implements DetachedSign {
|
||||||
|
|
||||||
private boolean armor = true;
|
private boolean armor = true;
|
||||||
private SignAs mode = SignAs.Binary;
|
private SignAs mode = SignAs.Binary;
|
||||||
private final SigningOptions signingOptions = new SigningOptions();
|
private final SigningOptions signingOptions = new SigningOptions();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sign noArmor() {
|
public DetachedSign noArmor() {
|
||||||
armor = false;
|
armor = false;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sign mode(SignAs mode) {
|
public DetachedSign mode(SignAs mode) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sign key(InputStream keyIn) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException {
|
public DetachedSign key(InputStream keyIn) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException {
|
||||||
try {
|
try {
|
||||||
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
||||||
|
|
||||||
|
@ -70,6 +70,11 @@ public class SignImpl implements Sign {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedSign withKeyPassword(byte[] password) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadyWithResult<SigningResult> data(InputStream data) throws IOException {
|
public ReadyWithResult<SigningResult> data(InputStream data) throws IOException {
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
@ -21,26 +21,26 @@ import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import sop.Verification;
|
import sop.Verification;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.Verify;
|
import sop.operation.DetachedVerify;
|
||||||
|
|
||||||
public class VerifyImpl implements Verify {
|
public class DetachedVerifyImpl implements DetachedVerify {
|
||||||
|
|
||||||
private final ConsumerOptions options = new ConsumerOptions();
|
private final ConsumerOptions options = new ConsumerOptions();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Verify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
public DetachedVerify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
options.verifyNotBefore(timestamp);
|
options.verifyNotBefore(timestamp);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Verify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
public DetachedVerify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
options.verifyNotAfter(timestamp);
|
options.verifyNotAfter(timestamp);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Verify cert(InputStream cert) throws SOPGPException.BadData {
|
public DetachedVerify cert(InputStream cert) throws SOPGPException.BadData {
|
||||||
PGPPublicKeyRingCollection certificates;
|
PGPPublicKeyRingCollection certificates;
|
||||||
try {
|
try {
|
||||||
certificates = PGPainless.readKeyRing().publicKeyRingCollection(cert);
|
certificates = PGPainless.readKeyRing().publicKeyRingCollection(cert);
|
||||||
|
@ -52,7 +52,7 @@ public class VerifyImpl implements Verify {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VerifyImpl signatures(InputStream signatures) throws SOPGPException.BadData {
|
public DetachedVerifyImpl signatures(InputStream signatures) throws SOPGPException.BadData {
|
||||||
try {
|
try {
|
||||||
options.addVerificationOfDetachedSignatures(signatures);
|
options.addVerificationOfDetachedSignatures(signatures);
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
|
@ -7,9 +7,13 @@ package org.pgpainless.sop;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
|
@ -20,7 +24,6 @@ import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
import org.pgpainless.encryption_signing.ProducerOptions;
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
import org.pgpainless.encryption_signing.SigningOptions;
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.exception.WrongPassphraseException;
|
import org.pgpainless.exception.WrongPassphraseException;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
import sop.util.ProxyOutputStream;
|
import sop.util.ProxyOutputStream;
|
||||||
import sop.Ready;
|
import sop.Ready;
|
||||||
|
@ -33,6 +36,9 @@ public class EncryptImpl implements Encrypt {
|
||||||
EncryptionOptions encryptionOptions = new EncryptionOptions();
|
EncryptionOptions encryptionOptions = new EncryptionOptions();
|
||||||
SigningOptions signingOptions = null;
|
SigningOptions signingOptions = null;
|
||||||
|
|
||||||
|
Set<PGPSecretKeyRing> signingKeys = new HashSet<>();
|
||||||
|
MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
|
||||||
|
|
||||||
private EncryptAs encryptAs = EncryptAs.Binary;
|
private EncryptAs encryptAs = EncryptAs.Binary;
|
||||||
boolean armor = true;
|
boolean armor = true;
|
||||||
|
|
||||||
|
@ -49,7 +55,7 @@ public class EncryptImpl implements Encrypt {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Encrypt signWith(InputStream keyIn) throws SOPGPException.KeyIsProtected, SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData {
|
public Encrypt signWith(InputStream keyIn) throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData {
|
||||||
try {
|
try {
|
||||||
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
||||||
if (keys.size() != 1) {
|
if (keys.size() != 1) {
|
||||||
|
@ -59,23 +65,20 @@ public class EncryptImpl implements Encrypt {
|
||||||
if (signingOptions == null) {
|
if (signingOptions == null) {
|
||||||
signingOptions = SigningOptions.get();
|
signingOptions = SigningOptions.get();
|
||||||
}
|
}
|
||||||
try {
|
signingKeys.add(keys.getKeyRings().next());
|
||||||
signingOptions.addInlineSignatures(
|
|
||||||
SecretKeyRingProtector.unprotectedKeys(),
|
|
||||||
keys,
|
|
||||||
(encryptAs == EncryptAs.Binary ? DocumentSignatureType.BINARY_DOCUMENT : DocumentSignatureType.CANONICAL_TEXT_DOCUMENT)
|
|
||||||
);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new SOPGPException.KeyCannotSign();
|
|
||||||
} catch (WrongPassphraseException e) {
|
|
||||||
throw new SOPGPException.KeyIsProtected();
|
|
||||||
}
|
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt withKeyPassword(byte[] password) {
|
||||||
|
String passphrase = new String(password, Charset.forName("UTF8"));
|
||||||
|
protector.addPassphrase(Passphrase.fromPassword(passphrase));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Encrypt withPassword(String password) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
public Encrypt withPassword(String password) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
encryptionOptions.addPassphrase(Passphrase.fromPassword(password));
|
encryptionOptions.addPassphrase(Passphrase.fromPassword(password));
|
||||||
|
@ -97,6 +100,26 @@ public class EncryptImpl implements Encrypt {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Ready plaintext(InputStream plaintext) throws IOException {
|
public Ready plaintext(InputStream plaintext) throws IOException {
|
||||||
|
for (PGPSecretKeyRing signingKey : signingKeys) {
|
||||||
|
protector.addSecretKey(signingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signingOptions != null) {
|
||||||
|
try {
|
||||||
|
signingOptions.addInlineSignatures(
|
||||||
|
protector,
|
||||||
|
signingKeys,
|
||||||
|
(encryptAs == EncryptAs.Binary ? DocumentSignatureType.BINARY_DOCUMENT : DocumentSignatureType.CANONICAL_TEXT_DOCUMENT)
|
||||||
|
);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new SOPGPException.KeyCannotSign();
|
||||||
|
} catch (WrongPassphraseException e) {
|
||||||
|
throw new SOPGPException.KeyIsProtected();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new SOPGPException.BadData(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ProducerOptions producerOptions = signingOptions != null ?
|
ProducerOptions producerOptions = signingOptions != null ?
|
||||||
ProducerOptions.signAndEncrypt(encryptionOptions, signingOptions) :
|
ProducerOptions.signAndEncrypt(encryptionOptions, signingOptions) :
|
||||||
ProducerOptions.encrypt(encryptionOptions);
|
ProducerOptions.encrypt(encryptionOptions);
|
||||||
|
@ -125,7 +148,6 @@ public class EncryptImpl implements Encrypt {
|
||||||
private static StreamEncoding encryptAsToStreamEncoding(EncryptAs encryptAs) {
|
private static StreamEncoding encryptAsToStreamEncoding(EncryptAs encryptAs) {
|
||||||
switch (encryptAs) {
|
switch (encryptAs) {
|
||||||
case Binary:
|
case Binary:
|
||||||
case MIME:
|
|
||||||
return StreamEncoding.BINARY;
|
return StreamEncoding.BINARY;
|
||||||
case Text:
|
case Text:
|
||||||
return StreamEncoding.UTF8;
|
return StreamEncoding.UTF8;
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.util.ArmorUtils;
|
import org.pgpainless.util.ArmorUtils;
|
||||||
|
import org.pgpainless.util.Passphrase;
|
||||||
import sop.Ready;
|
import sop.Ready;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.GenerateKey;
|
import sop.operation.GenerateKey;
|
||||||
|
@ -27,6 +28,7 @@ public class GenerateKeyImpl implements GenerateKey {
|
||||||
|
|
||||||
private boolean armor = true;
|
private boolean armor = true;
|
||||||
private final Set<String> userIds = new LinkedHashSet<>();
|
private final Set<String> userIds = new LinkedHashSet<>();
|
||||||
|
private Passphrase passphrase;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GenerateKey noArmor() {
|
public GenerateKey noArmor() {
|
||||||
|
@ -40,6 +42,12 @@ public class GenerateKeyImpl implements GenerateKey {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GenerateKey withKeyPassword(String password) {
|
||||||
|
this.passphrase = Passphrase.fromPassword(password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo {
|
public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo {
|
||||||
Iterator<String> userIdIterator = userIds.iterator();
|
Iterator<String> userIdIterator = userIds.iterator();
|
||||||
|
@ -50,7 +58,7 @@ public class GenerateKeyImpl implements GenerateKey {
|
||||||
PGPSecretKeyRing key;
|
PGPSecretKeyRing key;
|
||||||
try {
|
try {
|
||||||
key = PGPainless.generateKeyRing()
|
key = PGPainless.generateKeyRing()
|
||||||
.modernKeyRing(userIdIterator.next(), null);
|
.modernKeyRing(userIdIterator.next(), passphrase);
|
||||||
|
|
||||||
if (userIdIterator.hasNext()) {
|
if (userIdIterator.hasNext()) {
|
||||||
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(key);
|
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(key);
|
||||||
|
|
|
@ -20,14 +20,14 @@ import org.pgpainless.util.ArmoredOutputStreamFactory;
|
||||||
import sop.ReadyWithResult;
|
import sop.ReadyWithResult;
|
||||||
import sop.Signatures;
|
import sop.Signatures;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.DetachInbandSignatureAndMessage;
|
import sop.operation.InlineDetach;
|
||||||
|
|
||||||
public class DetachInbandSignatureAndMessageImpl implements DetachInbandSignatureAndMessage {
|
public class InlineDetachImpl implements InlineDetach {
|
||||||
|
|
||||||
private boolean armor = true;
|
private boolean armor = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachInbandSignatureAndMessage noArmor() {
|
public InlineDetach noArmor() {
|
||||||
this.armor = false;
|
this.armor = false;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.sop;
|
||||||
|
|
||||||
|
import sop.ReadyWithResult;
|
||||||
|
import sop.SigningResult;
|
||||||
|
import sop.enums.InlineSignAs;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.DetachedSign;
|
||||||
|
import sop.operation.InlineSign;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class InlineSignImpl implements InlineSign {
|
||||||
|
@Override
|
||||||
|
public DetachedSign mode(InlineSignAs mode) throws SOPGPException.UnsupportedOption {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedSign noArmor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign key(InputStream key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign withKeyPassword(byte[] password) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadyWithResult<SigningResult> data(InputStream data) throws IOException, SOPGPException.ExpectedText {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.sop;
|
||||||
|
|
||||||
|
import sop.ReadyWithResult;
|
||||||
|
import sop.Verification;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.InlineVerify;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InlineVerifyImpl implements InlineVerify {
|
||||||
|
@Override
|
||||||
|
public ReadyWithResult<List<Verification>> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify cert(InputStream cert) throws SOPGPException.BadData {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.sop;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
|
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
|
import org.pgpainless.key.info.KeyInfo;
|
||||||
|
import org.pgpainless.key.protection.CachingSecretKeyRingProtector;
|
||||||
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||||
|
import org.pgpainless.util.Passphrase;
|
||||||
|
|
||||||
|
public class MatchMakingSecretKeyRingProtector implements SecretKeyRingProtector {
|
||||||
|
|
||||||
|
private final Set<Passphrase> passphrases = new HashSet<>();
|
||||||
|
private final Set<PGPSecretKeyRing> keys = new HashSet<>();
|
||||||
|
private final CachingSecretKeyRingProtector protector = new CachingSecretKeyRingProtector();
|
||||||
|
|
||||||
|
public void addPassphrase(Passphrase passphrase) {
|
||||||
|
if (passphrase.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!passphrases.add(passphrase)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPSecretKeyRing key : keys) {
|
||||||
|
for (PGPSecretKey subkey : key) {
|
||||||
|
if (protector.hasPassphrase(subkey.getKeyID())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
testPassphrase(passphrase, subkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSecretKey(PGPSecretKeyRing key) {
|
||||||
|
if (!keys.add(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPSecretKey subkey : key) {
|
||||||
|
if (KeyInfo.isDecrypted(subkey)) {
|
||||||
|
protector.addPassphrase(subkey.getKeyID(), Passphrase.emptyPassphrase());
|
||||||
|
} else {
|
||||||
|
for (Passphrase passphrase : passphrases) {
|
||||||
|
testPassphrase(passphrase, subkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testPassphrase(Passphrase passphrase, PGPSecretKey subkey) {
|
||||||
|
try {
|
||||||
|
PBESecretKeyDecryptor decryptor = ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase);
|
||||||
|
UnlockSecretKey.unlockSecretKey(subkey, decryptor);
|
||||||
|
protector.addPassphrase(subkey.getKeyID(), passphrase);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
// wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPassphraseFor(Long keyId) {
|
||||||
|
return protector.hasPassphrase(keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public PBESecretKeyDecryptor getDecryptor(Long keyId) throws PGPException {
|
||||||
|
return protector.getDecryptor(keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public PBESecretKeyEncryptor getEncryptor(Long keyId) throws PGPException {
|
||||||
|
return protector.getEncryptor(keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
for (Passphrase passphrase : passphrases) {
|
||||||
|
passphrase.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPSecretKeyRing key : keys) {
|
||||||
|
protector.forgetPassphrase(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,14 @@ import sop.SOP;
|
||||||
import sop.operation.Armor;
|
import sop.operation.Armor;
|
||||||
import sop.operation.Dearmor;
|
import sop.operation.Dearmor;
|
||||||
import sop.operation.Decrypt;
|
import sop.operation.Decrypt;
|
||||||
import sop.operation.DetachInbandSignatureAndMessage;
|
import sop.operation.DetachedSign;
|
||||||
|
import sop.operation.DetachedVerify;
|
||||||
|
import sop.operation.InlineDetach;
|
||||||
import sop.operation.Encrypt;
|
import sop.operation.Encrypt;
|
||||||
import sop.operation.ExtractCert;
|
import sop.operation.ExtractCert;
|
||||||
import sop.operation.GenerateKey;
|
import sop.operation.GenerateKey;
|
||||||
import sop.operation.Sign;
|
import sop.operation.InlineSign;
|
||||||
import sop.operation.Verify;
|
import sop.operation.InlineVerify;
|
||||||
import sop.operation.Version;
|
import sop.operation.Version;
|
||||||
|
|
||||||
public class SOPImpl implements SOP {
|
public class SOPImpl implements SOP {
|
||||||
|
@ -34,13 +36,33 @@ public class SOPImpl implements SOP {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sign sign() {
|
public DetachedSign sign() {
|
||||||
return new SignImpl();
|
return detachedSign();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Verify verify() {
|
public DetachedSign detachedSign() {
|
||||||
return new VerifyImpl();
|
return new DetachedSignImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign inlineSign() {
|
||||||
|
return new InlineSignImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedVerify verify() {
|
||||||
|
return detachedVerify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedVerify detachedVerify() {
|
||||||
|
return new DetachedVerifyImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify inlineVerify() {
|
||||||
|
return new InlineVerifyImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +86,7 @@ public class SOPImpl implements SOP {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachInbandSignatureAndMessage detachInbandSignatureAndMessage() {
|
public InlineDetach inlineDetach() {
|
||||||
return new DetachInbandSignatureAndMessageImpl();
|
return new InlineDetachImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class ArmorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void armor() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
public void armor() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
byte[] data = PGPainless.generateKeyRing().modernKeyRing("Alice", null).getEncoded();
|
byte[] data = PGPainless.generateKeyRing().modernKeyRing("Alice").getEncoded();
|
||||||
byte[] knownGoodArmor = ArmorUtils.toAsciiArmoredString(data).getBytes(StandardCharsets.UTF_8);
|
byte[] knownGoodArmor = ArmorUtils.toAsciiArmoredString(data).getBytes(StandardCharsets.UTF_8);
|
||||||
byte[] armored = new SOPImpl()
|
byte[] armored = new SOPImpl()
|
||||||
.armor()
|
.armor()
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class DetachInbandSignatureAndMessageTest {
|
||||||
signingStream.close();
|
signingStream.close();
|
||||||
|
|
||||||
// actually detach the message
|
// actually detach the message
|
||||||
ByteArrayAndResult<Signatures> detachedMsg = sop.detachInbandSignatureAndMessage()
|
ByteArrayAndResult<Signatures> detachedMsg = sop.inlineDetach()
|
||||||
.message(out.toByteArray())
|
.message(out.toByteArray())
|
||||||
.toByteArrayAndResult();
|
.toByteArrayAndResult();
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
@ -24,12 +25,13 @@ import sop.exception.SOPGPException;
|
||||||
|
|
||||||
public class EncryptDecryptRoundTripTest {
|
public class EncryptDecryptRoundTripTest {
|
||||||
|
|
||||||
|
private static final Charset utf8 = Charset.forName("UTF8");
|
||||||
private static SOP sop;
|
private static SOP sop;
|
||||||
private static byte[] aliceKey;
|
private static byte[] aliceKey;
|
||||||
private static byte[] aliceCert;
|
private static byte[] aliceCert;
|
||||||
private static byte[] bobKey;
|
private static byte[] bobKey;
|
||||||
private static byte[] bobCert;
|
private static byte[] bobCert;
|
||||||
private static byte[] message = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
private static byte[] message = "Hello, World!\n".getBytes(utf8);
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setup() throws IOException {
|
public static void setup() throws IOException {
|
||||||
|
@ -218,8 +220,119 @@ public class EncryptDecryptRoundTripTest {
|
||||||
"=MUYS\n" +
|
"=MUYS\n" +
|
||||||
"-----END PGP PRIVATE KEY BLOCK-----";
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
|
||||||
|
String msg = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4Doj0CaB2GRvISAQdAhV5sjUCxanM68jG9qaq2rep1KKQx2o+9yrK0Rsrtqkww\n" +
|
||||||
|
"mb4uVv/SD3ixDztUSgUset0jeUeZHZAWfTB9cWawX4fiB2BdbcxhxFqQR8VPJ2SZ\n" +
|
||||||
|
"0jcB+wH1gq05AkMaCfoEIio3o3QcZq2In8tqj69U3AFRQApoH/p+ZLDz2pcnFBn+\n" +
|
||||||
|
"x1Y+C6wNg/3g\n" +
|
||||||
|
"=6vge\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
assertThrows(SOPGPException.KeyIsProtected.class, () -> sop.decrypt()
|
assertThrows(SOPGPException.KeyIsProtected.class, () -> sop.decrypt()
|
||||||
.withKey(passwordProtectedKey.getBytes(StandardCharsets.UTF_8)));
|
.withKey(passwordProtectedKey.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.ciphertext(msg.getBytes(utf8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptDecryptRoundTripWithProtectedKey() throws IOException {
|
||||||
|
byte[] passphrase = "sw0rdf1sh".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] key = sop.generateKey()
|
||||||
|
.userId("Alice <alice@pgpainless.org>")
|
||||||
|
.withKeyPassword(passphrase)
|
||||||
|
.generate().getBytes();
|
||||||
|
|
||||||
|
byte[] cert = sop.extractCert()
|
||||||
|
.key(key)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] plaintext = "Hello, World!\n".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] ciphertext = sop.encrypt()
|
||||||
|
.withCert(cert)
|
||||||
|
.plaintext(plaintext)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] decrypted = sop.decrypt()
|
||||||
|
.withKeyPassword(passphrase)
|
||||||
|
.withKey(key)
|
||||||
|
.ciphertext(ciphertext)
|
||||||
|
.toByteArrayAndResult()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayEquals(plaintext, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptDecryptRoundTripWithTwoProtectedKeysAndOnePassphrase() throws IOException {
|
||||||
|
byte[] passphrase1 = "sw0rdf1sh".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] key1 = sop.generateKey()
|
||||||
|
.userId("Alice <alice@pgpainless.org>")
|
||||||
|
.withKeyPassword(passphrase1)
|
||||||
|
.generate().getBytes();
|
||||||
|
|
||||||
|
byte[] cert1 = sop.extractCert()
|
||||||
|
.key(key1)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] passphrase2 = "fooBar".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] key2 = sop.generateKey()
|
||||||
|
.userId("Bob <bob@pgpainless.org>")
|
||||||
|
.withKeyPassword(passphrase2)
|
||||||
|
.generate().getBytes();
|
||||||
|
|
||||||
|
byte[] cert2 = sop.extractCert()
|
||||||
|
.key(key2)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] plaintext = "Hello, World!\n".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] ciphertext = sop.encrypt()
|
||||||
|
.withCert(cert1)
|
||||||
|
.withCert(cert2)
|
||||||
|
.plaintext(plaintext)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] decrypted = sop.decrypt()
|
||||||
|
.withKey(key1)
|
||||||
|
.withKey(key2)
|
||||||
|
.withKeyPassword(passphrase2)
|
||||||
|
.ciphertext(ciphertext)
|
||||||
|
.toByteArrayAndResult()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayEquals(plaintext, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptDecryptRoundTripFailsWithProtectedKeyAndWrongPassphrase() throws IOException {
|
||||||
|
byte[] passphrase = "sw0rdf1sh".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] key = sop.generateKey()
|
||||||
|
.userId("Alice <alice@pgpainless.org>")
|
||||||
|
.withKeyPassword(passphrase)
|
||||||
|
.generate().getBytes();
|
||||||
|
|
||||||
|
byte[] cert = sop.extractCert()
|
||||||
|
.key(key)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] plaintext = "Hello, World!\n".getBytes(utf8);
|
||||||
|
|
||||||
|
byte[] ciphertext = sop.encrypt()
|
||||||
|
.withCert(cert)
|
||||||
|
.plaintext(plaintext)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertThrows(SOPGPException.KeyIsProtected.class,
|
||||||
|
() -> sop.decrypt()
|
||||||
|
.withKeyPassword("foobar")
|
||||||
|
.withKey(key)
|
||||||
|
.ciphertext(ciphertext));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -12,6 +12,6 @@ allprojects {
|
||||||
slf4jVersion = '1.7.36'
|
slf4jVersion = '1.7.36'
|
||||||
logbackVersion = '1.2.11'
|
logbackVersion = '1.2.11'
|
||||||
junitVersion = '5.8.2'
|
junitVersion = '5.8.2'
|
||||||
sopJavaVersion = '1.2.3'
|
sopJavaVersion = '1.2.4-SNAPSHOT'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue