mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 03:17:58 +01:00
Experimental support for inline-sign, inline-verify
This commit is contained in:
parent
dd26b5230d
commit
0b69e18715
11 changed files with 222 additions and 87 deletions
|
@ -43,7 +43,7 @@ public class ArmorTest {
|
||||||
@FailOnSystemExit
|
@FailOnSystemExit
|
||||||
public void armorSecretKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
public void armorSecretKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||||
.modernKeyRing("alice@pgpainless.org", null);
|
.modernKeyRing("alice@pgpainless.org");
|
||||||
byte[] bytes = secretKey.getEncoded();
|
byte[] bytes = secretKey.getEncoded();
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(bytes));
|
System.setIn(new ByteArrayInputStream(bytes));
|
||||||
|
@ -59,7 +59,7 @@ public class ArmorTest {
|
||||||
@FailOnSystemExit
|
@FailOnSystemExit
|
||||||
public void armorPublicKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
public void armorPublicKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||||
.modernKeyRing("alice@pgpainless.org", null);
|
.modernKeyRing("alice@pgpainless.org");
|
||||||
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
||||||
byte[] bytes = publicKey.getEncoded();
|
byte[] bytes = publicKey.getEncoded();
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class DearmorTest {
|
||||||
@FailOnSystemExit
|
@FailOnSystemExit
|
||||||
public void dearmorSecretKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
public void dearmorSecretKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||||
.modernKeyRing("alice@pgpainless.org", null);
|
.modernKeyRing("alice@pgpainless.org");
|
||||||
String armored = PGPainless.asciiArmor(secretKey);
|
String armored = PGPainless.asciiArmor(secretKey);
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
@ -59,7 +59,7 @@ public class DearmorTest {
|
||||||
@FailOnSystemExit
|
@FailOnSystemExit
|
||||||
public void dearmorCertificate() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
public void dearmorCertificate() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||||
.modernKeyRing("alice@pgpainless.org", null);
|
.modernKeyRing("alice@pgpainless.org");
|
||||||
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
||||||
String armored = PGPainless.asciiArmor(certificate);
|
String armored = PGPainless.asciiArmor(certificate);
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ public class DetachInbandSignatureAndMessageTest {
|
||||||
|
|
||||||
// Detach
|
// Detach
|
||||||
File tempSigFile = new File(tempDir, "sig.out");
|
File tempSigFile = new File(tempDir, "sig.out");
|
||||||
PGPainlessCLI.main(new String[] {"detach-inband-signature-and-message", "--signatures-out=" + tempSigFile.getAbsolutePath()});
|
PGPainlessCLI.main(new String[] {"inline-detach", "--signatures-out=" + tempSigFile.getAbsolutePath()});
|
||||||
|
|
||||||
// Test equality with expected values
|
// Test equality with expected values
|
||||||
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
||||||
|
@ -150,7 +150,7 @@ public class DetachInbandSignatureAndMessageTest {
|
||||||
|
|
||||||
// Detach
|
// Detach
|
||||||
File tempSigFile = new File(tempDir, "sig.asc");
|
File tempSigFile = new File(tempDir, "sig.asc");
|
||||||
PGPainlessCLI.main(new String[] {"detach-inband-signature-and-message", "--signatures-out=" + tempSigFile.getAbsolutePath(), "--no-armor"});
|
PGPainlessCLI.main(new String[] {"inline-detach", "--signatures-out=" + tempSigFile.getAbsolutePath(), "--no-armor"});
|
||||||
|
|
||||||
// Test equality with expected values
|
// Test equality with expected values
|
||||||
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
||||||
|
@ -187,7 +187,7 @@ public class DetachInbandSignatureAndMessageTest {
|
||||||
// Detach
|
// Detach
|
||||||
File existingSigFile = new File(tempDir, "sig.existing");
|
File existingSigFile = new File(tempDir, "sig.existing");
|
||||||
assertTrue(existingSigFile.createNewFile());
|
assertTrue(existingSigFile.createNewFile());
|
||||||
PGPainlessCLI.main(new String[] {"detach-inband-signature-and-message", "--signatures-out=" + existingSigFile.getAbsolutePath()});
|
PGPainlessCLI.main(new String[] {"inline-detach", "--signatures-out=" + existingSigFile.getAbsolutePath()});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class SignVerifyTest {
|
||||||
File aliceKeyFile = new File(tempDir, "alice.key");
|
File aliceKeyFile = new File(tempDir, "alice.key");
|
||||||
assertTrue(aliceKeyFile.createNewFile());
|
assertTrue(aliceKeyFile.createNewFile());
|
||||||
PGPSecretKeyRing aliceKeys = PGPainless.generateKeyRing()
|
PGPSecretKeyRing aliceKeys = PGPainless.generateKeyRing()
|
||||||
.modernKeyRing("alice", null);
|
.modernKeyRing("alice");
|
||||||
OutputStream aliceKeyOut = new FileOutputStream(aliceKeyFile);
|
OutputStream aliceKeyOut = new FileOutputStream(aliceKeyFile);
|
||||||
Streams.pipeAll(new ByteArrayInputStream(aliceKeys.getEncoded()), aliceKeyOut);
|
Streams.pipeAll(new ByteArrayInputStream(aliceKeys.getEncoded()), aliceKeyOut);
|
||||||
aliceKeyOut.close();
|
aliceKeyOut.close();
|
||||||
|
@ -108,7 +108,7 @@ public class SignVerifyTest {
|
||||||
String[] split = verification.split(" ");
|
String[] split = verification.split(" ");
|
||||||
OpenPgpV4Fingerprint primaryKeyFingerprint = new OpenPgpV4Fingerprint(aliceKeys);
|
OpenPgpV4Fingerprint primaryKeyFingerprint = new OpenPgpV4Fingerprint(aliceKeys);
|
||||||
OpenPgpV4Fingerprint signingKeyFingerprint = new OpenPgpV4Fingerprint(new KeyRingInfo(alicePub, new Date()).getSigningSubkeys().get(0));
|
OpenPgpV4Fingerprint signingKeyFingerprint = new OpenPgpV4Fingerprint(new KeyRingInfo(alicePub, new Date()).getSigningSubkeys().get(0));
|
||||||
assertEquals(signingKeyFingerprint.toString(), split[1].trim());
|
assertEquals(signingKeyFingerprint.toString(), split[1].trim(), verification);
|
||||||
assertEquals(primaryKeyFingerprint.toString(), split[2].trim());
|
assertEquals(primaryKeyFingerprint.toString(), split[2].trim());
|
||||||
|
|
||||||
// Test micalg output
|
// Test micalg output
|
||||||
|
|
|
@ -134,7 +134,7 @@ public class SignUsingPublicKeyBehaviorTest {
|
||||||
assertTrue(sigFile.createNewFile());
|
assertTrue(sigFile.createNewFile());
|
||||||
FileOutputStream sigOut = new FileOutputStream(sigFile);
|
FileOutputStream sigOut = new FileOutputStream(sigFile);
|
||||||
System.setOut(new PrintStream(sigOut));
|
System.setOut(new PrintStream(sigOut));
|
||||||
PGPainlessCLI.execute("sign", "--armor", aliceKeyFile.getAbsolutePath());
|
PGPainlessCLI.main(new String[] {"sign", "--armor", aliceKeyFile.getAbsolutePath()});
|
||||||
|
|
||||||
System.setIn(originalIn);
|
System.setIn(originalIn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,7 @@ import java.io.OutputStream;
|
||||||
import java.nio.charset.Charset;
|
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;
|
||||||
|
@ -39,7 +37,6 @@ 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();
|
private final MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -105,7 +102,8 @@ public class DecryptImpl implements Decrypt {
|
||||||
PGPSecretKeyRingCollection secretKeyCollection = PGPainless.readKeyRing()
|
PGPSecretKeyRingCollection secretKeyCollection = PGPainless.readKeyRing()
|
||||||
.secretKeyRingCollection(keyIn);
|
.secretKeyRingCollection(keyIn);
|
||||||
for (PGPSecretKeyRing key : secretKeyCollection) {
|
for (PGPSecretKeyRing key : secretKeyCollection) {
|
||||||
keys.add(key);
|
protector.addSecretKey(key);
|
||||||
|
consumerOptions.addDecryptionKey(key, protector);
|
||||||
}
|
}
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
|
@ -124,10 +122,6 @@ public class DecryptImpl implements Decrypt {
|
||||||
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.");
|
||||||
|
@ -145,6 +139,7 @@ public class DecryptImpl implements Decrypt {
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
// Forget passphrases after decryption
|
||||||
protector.clear();
|
protector.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream;
|
||||||
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.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -24,9 +25,8 @@ import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
import org.pgpainless.encryption_signing.SigningOptions;
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.exception.KeyException;
|
import org.pgpainless.exception.KeyException;
|
||||||
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.ArmoredOutputStreamFactory;
|
import org.pgpainless.util.ArmoredOutputStreamFactory;
|
||||||
|
import org.pgpainless.util.Passphrase;
|
||||||
import sop.MicAlg;
|
import sop.MicAlg;
|
||||||
import sop.ReadyWithResult;
|
import sop.ReadyWithResult;
|
||||||
import sop.SigningResult;
|
import sop.SigningResult;
|
||||||
|
@ -39,6 +39,7 @@ 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();
|
||||||
|
private final MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign noArmor() {
|
public DetachedSign noArmor() {
|
||||||
|
@ -58,11 +59,8 @@ public class DetachedSignImpl implements DetachedSign {
|
||||||
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
||||||
|
|
||||||
for (PGPSecretKeyRing key : keys) {
|
for (PGPSecretKeyRing key : keys) {
|
||||||
KeyRingInfo info = new KeyRingInfo(key);
|
protector.addSecretKey(key);
|
||||||
if (!info.isFullyDecrypted()) {
|
signingOptions.addDetachedSignature(protector, key, modeToSigType(mode));
|
||||||
throw new SOPGPException.KeyIsProtected();
|
|
||||||
}
|
|
||||||
signingOptions.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), key, modeToSigType(mode));
|
|
||||||
}
|
}
|
||||||
} catch (PGPException | KeyException e) {
|
} catch (PGPException | KeyException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
|
@ -72,7 +70,9 @@ public class DetachedSignImpl implements DetachedSign {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign withKeyPassword(byte[] password) {
|
public DetachedSign withKeyPassword(byte[] password) {
|
||||||
return null;
|
String string = new String(password, Charset.forName("UTF8"));
|
||||||
|
protector.addPassphrase(Passphrase.fromPassword(string));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,6 +96,9 @@ public class DetachedSignImpl implements DetachedSign {
|
||||||
signingStream.close();
|
signingStream.close();
|
||||||
EncryptionResult encryptionResult = signingStream.getResult();
|
EncryptionResult encryptionResult = signingStream.getResult();
|
||||||
|
|
||||||
|
// forget passphrases
|
||||||
|
protector.clear();
|
||||||
|
|
||||||
List<PGPSignature> signatures = new ArrayList<>();
|
List<PGPSignature> signatures = new ArrayList<>();
|
||||||
for (SubkeyIdentifier key : encryptionResult.getDetachedSignatures().keySet()) {
|
for (SubkeyIdentifier key : encryptionResult.getDetachedSignatures().keySet()) {
|
||||||
signatures.addAll(encryptionResult.getDetachedSignatures().get(key));
|
signatures.addAll(encryptionResult.getDetachedSignatures().get(key));
|
||||||
|
|
|
@ -8,8 +8,6 @@ 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.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;
|
||||||
|
@ -25,18 +23,16 @@ 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.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
import sop.util.ProxyOutputStream;
|
|
||||||
import sop.Ready;
|
import sop.Ready;
|
||||||
import sop.enums.EncryptAs;
|
import sop.enums.EncryptAs;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.Encrypt;
|
import sop.operation.Encrypt;
|
||||||
|
import sop.util.ProxyOutputStream;
|
||||||
|
|
||||||
public class EncryptImpl implements Encrypt {
|
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();
|
MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
|
||||||
|
|
||||||
private EncryptAs encryptAs = EncryptAs.Binary;
|
private EncryptAs encryptAs = EncryptAs.Binary;
|
||||||
|
@ -55,17 +51,34 @@ public class EncryptImpl implements Encrypt {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Encrypt signWith(InputStream keyIn) throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData {
|
public Encrypt signWith(InputStream keyIn)
|
||||||
|
throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData {
|
||||||
|
if (signingOptions == null) {
|
||||||
|
signingOptions = SigningOptions.get();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
||||||
if (keys.size() != 1) {
|
if (keys.size() != 1) {
|
||||||
throw new SOPGPException.BadData(new AssertionError("Exactly one secret key at a time expected. Got " + keys.size()));
|
throw new SOPGPException.BadData(new AssertionError("Exactly one secret key at a time expected. Got " + keys.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signingOptions == null) {
|
PGPSecretKeyRing signingKey = keys.iterator().next();
|
||||||
signingOptions = SigningOptions.get();
|
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);
|
||||||
}
|
}
|
||||||
signingKeys.add(keys.getKeyRings().next());
|
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
}
|
}
|
||||||
|
@ -100,26 +113,6 @@ 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);
|
||||||
|
|
|
@ -4,39 +4,139 @@
|
||||||
|
|
||||||
package org.pgpainless.sop;
|
package org.pgpainless.sop;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.algorithm.DocumentSignatureType;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionResult;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
|
import org.pgpainless.exception.KeyException;
|
||||||
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
|
import org.pgpainless.util.Passphrase;
|
||||||
|
import sop.MicAlg;
|
||||||
import sop.ReadyWithResult;
|
import sop.ReadyWithResult;
|
||||||
import sop.SigningResult;
|
import sop.SigningResult;
|
||||||
import sop.enums.InlineSignAs;
|
import sop.enums.InlineSignAs;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.DetachedSign;
|
|
||||||
import sop.operation.InlineSign;
|
import sop.operation.InlineSign;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public class InlineSignImpl implements InlineSign {
|
public class InlineSignImpl implements InlineSign {
|
||||||
|
|
||||||
|
private boolean armor = true;
|
||||||
|
private InlineSignAs mode = InlineSignAs.Binary;
|
||||||
|
private final SigningOptions signingOptions = new SigningOptions();
|
||||||
|
private final MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign mode(InlineSignAs mode) throws SOPGPException.UnsupportedOption {
|
public InlineSign mode(InlineSignAs mode) throws SOPGPException.UnsupportedOption {
|
||||||
return null;
|
this.mode = mode;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign noArmor() {
|
public InlineSign noArmor() {
|
||||||
return null;
|
this.armor = false;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineSign key(InputStream key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException {
|
public InlineSign key(InputStream keyIn) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException {
|
||||||
return null;
|
try {
|
||||||
|
PGPSecretKeyRingCollection keys = PGPainless.readKeyRing().secretKeyRingCollection(keyIn);
|
||||||
|
|
||||||
|
for (PGPSecretKeyRing key : keys) {
|
||||||
|
protector.addSecretKey(key);
|
||||||
|
if (mode == InlineSignAs.CleartextSigned) {
|
||||||
|
signingOptions.addDetachedSignature(protector, key, DocumentSignatureType.BINARY_DOCUMENT);
|
||||||
|
} else {
|
||||||
|
signingOptions.addInlineSignature(protector, key, modeToSigType(mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (PGPException | KeyException e) {
|
||||||
|
throw new SOPGPException.BadData(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineSign withKeyPassword(byte[] password) {
|
public InlineSign withKeyPassword(byte[] password) {
|
||||||
return null;
|
String string = new String(password, Charset.forName("UTF8"));
|
||||||
|
protector.addPassphrase(Passphrase.fromPassword(string));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadyWithResult<SigningResult> data(InputStream data) throws IOException, SOPGPException.ExpectedText {
|
public ReadyWithResult<SigningResult> data(InputStream data) throws IOException, SOPGPException.ExpectedText {
|
||||||
return null;
|
|
||||||
|
ProducerOptions producerOptions = ProducerOptions.sign(signingOptions);
|
||||||
|
if (mode == InlineSignAs.CleartextSigned) {
|
||||||
|
producerOptions.setCleartextSigned();
|
||||||
|
producerOptions.setAsciiArmor(true);
|
||||||
|
} else {
|
||||||
|
producerOptions.setAsciiArmor(armor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReadyWithResult<SigningResult>() {
|
||||||
|
@Override
|
||||||
|
public SigningResult writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature {
|
||||||
|
try {
|
||||||
|
EncryptionStream signingStream = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(outputStream)
|
||||||
|
.withOptions(producerOptions);
|
||||||
|
|
||||||
|
if (signingStream.isClosed()) {
|
||||||
|
throw new IllegalStateException("EncryptionStream is already closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Streams.pipeAll(data, signingStream);
|
||||||
|
signingStream.close();
|
||||||
|
EncryptionResult encryptionResult = signingStream.getResult();
|
||||||
|
|
||||||
|
// forget passphrases
|
||||||
|
protector.clear();
|
||||||
|
|
||||||
|
List<PGPSignature> signatures = new ArrayList<>();
|
||||||
|
for (SubkeyIdentifier key : encryptionResult.getDetachedSignatures().keySet()) {
|
||||||
|
signatures.addAll(encryptionResult.getDetachedSignatures().get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SigningResult.builder()
|
||||||
|
.setMicAlg(micAlgFromSignatures(signatures))
|
||||||
|
.build();
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private MicAlg micAlgFromSignatures(Iterable<PGPSignature> signatures) {
|
||||||
|
int algorithmId = 0;
|
||||||
|
for (PGPSignature signature : signatures) {
|
||||||
|
int sigAlg = signature.getHashAlgorithm();
|
||||||
|
if (algorithmId == 0 || algorithmId == sigAlg) {
|
||||||
|
algorithmId = sigAlg;
|
||||||
|
} else {
|
||||||
|
return MicAlg.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return algorithmId == 0 ? MicAlg.empty() : MicAlg.fromHashAlgorithmId(algorithmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DocumentSignatureType modeToSigType(InlineSignAs mode) {
|
||||||
|
return mode == InlineSignAs.Binary ? DocumentSignatureType.BINARY_DOCUMENT
|
||||||
|
: DocumentSignatureType.CANONICAL_TEXT_DOCUMENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,15 @@
|
||||||
|
|
||||||
package org.pgpainless.sop;
|
package org.pgpainless.sop;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.decryption_verification.ConsumerOptions;
|
||||||
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import sop.ReadyWithResult;
|
import sop.ReadyWithResult;
|
||||||
import sop.Verification;
|
import sop.Verification;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
|
@ -11,27 +20,75 @@ import sop.operation.InlineVerify;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class InlineVerifyImpl implements InlineVerify {
|
public class InlineVerifyImpl implements InlineVerify {
|
||||||
@Override
|
|
||||||
public ReadyWithResult<List<Verification>> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
private final ConsumerOptions options = new ConsumerOptions();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineVerify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
public InlineVerify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
return null;
|
options.verifyNotBefore(timestamp);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineVerify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
public InlineVerify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
return null;
|
options.verifyNotAfter(timestamp);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineVerify cert(InputStream cert) throws SOPGPException.BadData {
|
public InlineVerify cert(InputStream cert) throws SOPGPException.BadData {
|
||||||
return null;
|
PGPPublicKeyRingCollection certificates;
|
||||||
|
try {
|
||||||
|
certificates = PGPainless.readKeyRing().publicKeyRingCollection(cert);
|
||||||
|
} catch (IOException | PGPException e) {
|
||||||
|
throw new SOPGPException.BadData(e);
|
||||||
|
}
|
||||||
|
options.addVerificationCerts(certificates);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadyWithResult<List<Verification>> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
||||||
|
return new ReadyWithResult<List<Verification>>() {
|
||||||
|
@Override
|
||||||
|
public List<Verification> writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature {
|
||||||
|
DecryptionStream decryptionStream;
|
||||||
|
try {
|
||||||
|
decryptionStream = PGPainless.decryptAndOrVerify()
|
||||||
|
.onInputStream(data)
|
||||||
|
.withOptions(options);
|
||||||
|
|
||||||
|
Streams.pipeAll(decryptionStream, outputStream);
|
||||||
|
decryptionStream.close();
|
||||||
|
|
||||||
|
OpenPgpMetadata metadata = decryptionStream.getResult();
|
||||||
|
List<Verification> verificationList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) {
|
||||||
|
PGPSignature signature = metadata.getVerifiedSignatures().get(verifiedSigningKey);
|
||||||
|
verificationList.add(new Verification(
|
||||||
|
signature.getCreationTime(),
|
||||||
|
verifiedSigningKey.getSubkeyFingerprint().toString(),
|
||||||
|
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.getCertificates().isEmpty()) {
|
||||||
|
if (verificationList.isEmpty()) {
|
||||||
|
throw new SOPGPException.NoSignature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return verificationList;
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new SOPGPException.BadData(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
import org.pgpainless.algorithm.SignatureType;
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
import sop.SOP;
|
import sop.SOP;
|
||||||
|
@ -128,13 +124,4 @@ public class SignTest {
|
||||||
assertEquals(SignatureType.CANONICAL_TEXT_DOCUMENT.getCode(), sig.getSignatureType());
|
assertEquals(SignatureType.CANONICAL_TEXT_DOCUMENT.getCode(), sig.getSignatureType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void rejectEncryptedKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
|
||||||
PGPSecretKeyRing key = PGPainless.generateKeyRing()
|
|
||||||
.modernKeyRing("Alice", "passphrase");
|
|
||||||
byte[] bytes = key.getEncoded();
|
|
||||||
|
|
||||||
assertThrows(SOPGPException.KeyIsProtected.class, () -> sop.sign().key(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue