diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/EncryptImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/EncryptImpl.java index 3c5f8e8a..0843ae08 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/EncryptImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/EncryptImpl.java @@ -8,6 +8,8 @@ import java.io.IOException; import java.io.InputStream; 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.PGPPublicKeyRingCollection; @@ -23,6 +25,8 @@ import org.pgpainless.encryption_signing.ProducerOptions; import org.pgpainless.encryption_signing.SigningOptions; import org.pgpainless.exception.KeyException; import org.pgpainless.exception.WrongPassphraseException; +import org.pgpainless.key.OpenPgpFingerprint; +import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.util.Passphrase; import sop.Ready; import sop.enums.EncryptAs; @@ -35,6 +39,7 @@ public class EncryptImpl implements Encrypt { EncryptionOptions encryptionOptions = new EncryptionOptions(); SigningOptions signingOptions = null; MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector(); + private final Set signingKeys = new HashSet<>(); private EncryptAs encryptAs = EncryptAs.Binary; boolean armor = true; @@ -63,23 +68,15 @@ public class EncryptImpl implements Encrypt { if (keys.size() != 1) { throw new SOPGPException.BadData(new AssertionError("Exactly one secret key at a time expected. Got " + keys.size())); } - PGPSecretKeyRing signingKey = keys.iterator().next(); - protector.addSecretKey(signingKey); - try { - signingOptions.addInlineSignature( - protector, - signingKey, - (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); + KeyRingInfo info = PGPainless.inspectKeyRing(signingKey); + if (info.getSigningSubkeys().isEmpty()) { + throw new SOPGPException.KeyCannotSign("Key " + OpenPgpFingerprint.of(signingKey) + " cannot sign."); } + + protector.addSecretKey(signingKey); + signingKeys.add(signingKey); } catch (IOException | PGPException e) { throw new SOPGPException.BadData(e); } @@ -122,6 +119,22 @@ public class EncryptImpl implements Encrypt { producerOptions.setAsciiArmor(armor); producerOptions.setEncoding(encryptAsToStreamEncoding(encryptAs)); + for (PGPSecretKeyRing signingKey : signingKeys) { + try { + signingOptions.addInlineSignature( + protector, + signingKey, + (encryptAs == EncryptAs.Binary ? DocumentSignatureType.BINARY_DOCUMENT : DocumentSignatureType.CANONICAL_TEXT_DOCUMENT) + ); + } catch (KeyException.UnacceptableSigningKeyException e) { + throw new SOPGPException.KeyCannotSign(); + } catch (WrongPassphraseException e) { + throw new SOPGPException.KeyIsProtected(); + } catch (PGPException e) { + throw new SOPGPException.BadData(e); + } + } + try { ProxyOutputStream proxy = new ProxyOutputStream(); EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/EncryptDecryptRoundTripTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/EncryptDecryptRoundTripTest.java index 45a26586..7bc5bfdb 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/EncryptDecryptRoundTripTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/EncryptDecryptRoundTripTest.java @@ -38,6 +38,7 @@ public class EncryptDecryptRoundTripTest { sop = new SOPImpl(); aliceKey = sop.generateKey() .userId("Alice ") + .withKeyPassword("wonderland.is.c00l") .generate() .getBytes(); aliceCert = sop.extractCert() @@ -56,6 +57,7 @@ public class EncryptDecryptRoundTripTest { public void basicRoundTripWithKey() throws IOException, SOPGPException.KeyCannotSign { byte[] encrypted = sop.encrypt() .signWith(aliceKey) + .withKeyPassword("wonderland.is.c00l") .withCert(aliceCert) .withCert(bobCert) .plaintext(message) @@ -426,6 +428,15 @@ public class EncryptDecryptRoundTripTest { assertArrayEquals(message, bytesAndResult.getBytes()); } + @Test + public void testEncryptWithWrongPassphraseThrowsKeyIsProtected() { + assertThrows(SOPGPException.KeyIsProtected.class, () -> sop.encrypt() + .withKeyPassword("falsePassphrase") + .signWith(aliceKey) + .withCert(bobCert) + .plaintext(message)); + } + @Test public void testDecryptionWithSessionKey_VerificationWithCert() throws IOException { byte[] plaintext = "This is a test message.\nSit back and relax.\n".getBytes(StandardCharsets.UTF_8); diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/IncapableKeysTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/IncapableKeysTest.java index a50acee1..5139bbf6 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/IncapableKeysTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/IncapableKeysTest.java @@ -64,4 +64,9 @@ public class IncapableKeysTest { assertThrows(SOPGPException.KeyCannotSign.class, () -> sop.detachedSign().key(nonSigningKey)); assertThrows(SOPGPException.KeyCannotSign.class, () -> sop.inlineSign().key(nonSigningKey)); } + + @Test + public void encryptAndSignWithNonSigningKeyFails() { + assertThrows(SOPGPException.KeyCannotSign.class, () -> sop.encrypt().signWith(nonSigningKey)); + } }