1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-23 03:17:58 +01:00

Use OpenPgpV4Fingerprint class

This commit is contained in:
Paul Schaub 2018-07-08 19:31:53 +02:00
parent d53501c9cd
commit fb2db94839
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
8 changed files with 171 additions and 59 deletions

View file

@ -48,6 +48,7 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
public final class DecryptionStreamFactory {
@ -63,7 +64,7 @@ public final class DecryptionStreamFactory {
private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder();
private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider();
private final KeyFingerPrintCalculator fingerCalc = new BcKeyFingerprintCalculator();
private final Map<Long, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>();
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>();
private DecryptionStreamFactory(PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingProtector decryptor,
@ -162,7 +163,7 @@ public final class DecryptionStreamFactory {
LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId));
encryptedSessionKey = encryptedData;
decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId));
resultBuilder.setDecryptionKeyId(keyId);
resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey));
}
}
@ -198,7 +199,7 @@ public final class DecryptionStreamFactory {
while (iterator.hasNext()) {
PGPOnePassSignature signature = iterator.next();
final long keyId = signature.getKeyID();
resultBuilder.addSignatureKeyId(keyId);
resultBuilder.addUnverifiedSignatureKeyId(keyId);
LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId));
@ -236,7 +237,7 @@ public final class DecryptionStreamFactory {
}
signature.init(verifierBuilderProvider, verificationKey);
verifiableOnePassSignatures.put(keyId, signature);
verifiableOnePassSignatures.put(new OpenPgpV4Fingerprint(verificationKey), signature);
}
}
}

View file

@ -19,36 +19,39 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
public class PainlessResult {
private final Set<Long> recipientKeyIds;
private final Long decryptionKeyId;
private final OpenPgpV4Fingerprint decryptionFingerprint;
private final Set<Long> unverifiedSignatureKeyIds;
private final Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints;
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
private final CompressionAlgorithm compressionAlgorithm;
private final boolean integrityProtected;
private final Set<Long> signatureKeyIds;
private final Set<Long> verifiedSignatureKeyIds;
public PainlessResult(Set<Long> recipientKeyIds,
Long decryptionKeyId,
OpenPgpV4Fingerprint decryptionFingerprint,
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
CompressionAlgorithm algorithm,
boolean integrityProtected,
Set<Long> signatureKeyIds,
Set<Long> verifiedSignatureKeyIds) {
Set<Long> unverifiedSignatureKeyIds,
Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints) {
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
this.decryptionKeyId = decryptionKeyId;
this.decryptionFingerprint = decryptionFingerprint;
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
this.compressionAlgorithm = algorithm;
this.integrityProtected = integrityProtected;
this.signatureKeyIds = Collections.unmodifiableSet(signatureKeyIds);
this.verifiedSignatureKeyIds = Collections.unmodifiableSet(verifiedSignatureKeyIds);
this.unverifiedSignatureKeyIds = Collections.unmodifiableSet(unverifiedSignatureKeyIds);
this.verifiedSignaturesFingerprints = Collections.unmodifiableSet(verifiedSignaturesFingerprints);
}
public Set<Long> getRecipientKeyIds() {
@ -59,8 +62,8 @@ public class PainlessResult {
return !getRecipientKeyIds().isEmpty();
}
public Long getDecryptionKeyId() {
return decryptionKeyId;
public OpenPgpV4Fingerprint getDecryptionFingerprint() {
return decryptionFingerprint;
}
public SymmetricKeyAlgorithm getSymmetricKeyAlgorithm() {
@ -75,26 +78,26 @@ public class PainlessResult {
return integrityProtected;
}
public Set<Long> getAllSignatureKeyIds() {
return signatureKeyIds;
public Set<Long> getAllSignatureKeyFingerprints() {
return unverifiedSignatureKeyIds;
}
public boolean isSigned() {
return !signatureKeyIds.isEmpty();
return !unverifiedSignatureKeyIds.isEmpty();
}
public Set<Long> getVerifiedSignatureKeyIds() {
return verifiedSignatureKeyIds;
public Set<OpenPgpV4Fingerprint> getVerifiedSignaturesFingerprints() {
return verifiedSignaturesFingerprints;
}
public boolean isVerified() {
return !verifiedSignatureKeyIds.isEmpty();
return !verifiedSignaturesFingerprints.isEmpty();
}
public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) {
public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) throws PGPException {
for (PGPPublicKey key : publicKeys) {
long id = key.getKeyID();
if (verifiedSignatureKeyIds.contains(id)) {
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(key);
if (verifiedSignaturesFingerprints.contains(fingerprint)) {
return true;
}
}
@ -107,21 +110,21 @@ public class PainlessResult {
static class Builder {
private final Set<Long> recipientKeyIds = new HashSet<>();
private Long decryptionKeyId;
private final Set<Long> recipientFingerprints = new HashSet<>();
private OpenPgpV4Fingerprint decryptionFingerprint;
private final Set<Long> unverifiedSignatureKeyIds = new HashSet<>();
private final Set<OpenPgpV4Fingerprint> verifiedSignatureFingerprints = new HashSet<>();
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
private boolean integrityProtected = false;
private final Set<Long> signatureKeyIds = new HashSet<>();
private final Set<Long> verifiedSignatureKeyIds = new HashSet<>();
public Builder addRecipientKeyId(long id) {
this.recipientKeyIds.add(id);
public Builder addRecipientKeyId(Long keyId) {
this.recipientFingerprints.add(keyId);
return this;
}
public Builder setDecryptionKeyId(long id) {
this.decryptionKeyId = id;
public Builder setDecryptionFingerprint(OpenPgpV4Fingerprint fingerprint) {
this.decryptionFingerprint = fingerprint;
return this;
}
@ -130,13 +133,13 @@ public class PainlessResult {
return this;
}
public Builder addSignatureKeyId(long id) {
this.signatureKeyIds.add(id);
public Builder addUnverifiedSignatureKeyId(Long keyId) {
this.unverifiedSignatureKeyIds.add(keyId);
return this;
}
public Builder addVerifiedSignatureKeyId(long id) {
this.verifiedSignatureKeyIds.add(id);
public Builder addVerifiedSignatureFingerprint(OpenPgpV4Fingerprint fingerprint) {
this.verifiedSignatureFingerprints.add(fingerprint);
return this;
}
@ -151,7 +154,7 @@ public class PainlessResult {
}
public PainlessResult build() {
return new PainlessResult(recipientKeyIds, decryptionKeyId, symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected, signatureKeyIds, verifiedSignatureKeyIds);
return new PainlessResult(recipientFingerprints, decryptionFingerprint, symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected, unverifiedSignatureKeyIds, verifiedSignatureFingerprints);
}
}
}

View file

@ -28,6 +28,7 @@ import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
public class SignatureVerifyingInputStream extends FilterInputStream {
@ -35,14 +36,14 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
private static final Level LEVEL = Level.INFO;
private final PGPObjectFactory objectFactory;
private final Map<Long, PGPOnePassSignature> onePassSignatures;
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures;
private final PainlessResult.Builder resultBuilder;
private boolean validated = false;
protected SignatureVerifyingInputStream(InputStream inputStream,
PGPObjectFactory objectFactory,
Map<Long, PGPOnePassSignature> onePassSignatures,
Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures,
PainlessResult.Builder resultBuilder) {
super(inputStream);
this.objectFactory = objectFactory;
@ -94,16 +95,25 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
}
for (PGPSignature signature : signatureList) {
PGPOnePassSignature onePassSignature = onePassSignatures.get(signature.getKeyID());
if (onePassSignature == null) {
OpenPgpV4Fingerprint fingerprint = null;
for (OpenPgpV4Fingerprint f : onePassSignatures.keySet()) {
if (f.getKeyId() == signature.getKeyID()) {
fingerprint = f;
break;
}
}
PGPOnePassSignature onePassSignature;
if (fingerprint == null || (onePassSignature = onePassSignatures.get(fingerprint)) == null) {
LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip");
continue;
}
if (!onePassSignature.verify(signature)) {
throw new SignatureException("Bad Signature of key " + signature.getKeyID());
} else {
LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID()));
resultBuilder.addVerifiedSignatureKeyId(signature.getKeyID());
resultBuilder.addVerifiedSignatureFingerprint(fingerprint);
}
}
} catch (PGPException | SignatureException e) {

View file

@ -1,5 +1,4 @@
/**
*
/*
* Copyright 2018 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +15,7 @@
*/
package org.pgpainless.pgpainless.key;
import javax.xml.bind.DatatypeConverter;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
@ -93,7 +92,12 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
* @return key id
*/
public long getKeyId() {
byte[] bytes = DatatypeConverter.parseHexBinary(this.toString());
byte[] bytes = new BigInteger(toString(), 16).toByteArray();
if (bytes.length != toString().length() / 2) {
bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
}
byte[] lower8Bytes = Arrays.copyOfRange(bytes, 12, 20);
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
byteBuffer.put(lower8Bytes);

View file

@ -15,14 +15,19 @@
*/
package org.pgpainless.pgpainless.key.collection;
import java.util.Objects;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.pgpainless.pgpainless.PGPainless;
public class KeyRingCollection {
@ -36,6 +41,20 @@ public class KeyRingCollection {
this.secretKeys = secretKeyRings;
}
public KeyRingCollection(File pubRingFile, File secRingFile) throws IOException, PGPException {
if (pubRingFile != null) {
InputStream pubRingIn = new FileInputStream(pubRingFile);
this.publicKeys = PGPainless.readKeyRing().publicKeyRingCollection(pubRingIn);
pubRingIn.close();
}
if (secRingFile != null) {
InputStream secRingIn = new FileInputStream(secRingFile);
this.secretKeys = PGPainless.readKeyRing().secretKeyRingCollection(secRingIn);
secRingIn.close();
}
}
public KeyRingCollection(PGPPublicKeyRingCollection publicKeyRings) {
this(publicKeyRings, null);
}
@ -45,7 +64,12 @@ public class KeyRingCollection {
}
public void importPublicKeys(PGPPublicKeyRingCollection publicKeyRings) {
for (PGPPublicKeyRing keyRing : Objects.requireNonNull(publicKeyRings)) {
if (this.publicKeys == null) {
this.publicKeys = publicKeyRings;
return;
}
for (PGPPublicKeyRing keyRing : publicKeyRings) {
try {
this.publicKeys = PGPPublicKeyRingCollection.addPublicKeyRing(this.publicKeys, keyRing);
} catch (IllegalArgumentException e) {
@ -57,7 +81,12 @@ public class KeyRingCollection {
}
public void importSecretKeys(PGPSecretKeyRingCollection secretKeyRings) {
for (PGPSecretKeyRing keyRing : Objects.requireNonNull(secretKeyRings)) {
if (this.secretKeys == null) {
this.secretKeys = secretKeyRings;
return;
}
for (PGPSecretKeyRing keyRing : secretKeyRings) {
try {
this.secretKeys = PGPSecretKeyRingCollection.addSecretKeyRing(this.secretKeys, keyRing);
} catch (IllegalArgumentException e) {

View file

@ -27,7 +27,6 @@ import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -43,13 +42,13 @@ import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.pgpainless.decryption_verification.PainlessResult;
import org.pgpainless.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector;
import org.pgpainless.pgpainless.key.generation.KeySpec;
import org.pgpainless.pgpainless.key.generation.type.ElGamal_GENERAL;
import org.pgpainless.pgpainless.key.generation.type.RSA_GENERAL;
import org.pgpainless.pgpainless.key.generation.type.length.ElGamalLength;
import org.pgpainless.pgpainless.key.generation.type.length.RsaLength;
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector;
import org.pgpainless.pgpainless.util.BCUtil;
public class EncryptDecryptTest extends AbstractPGPainlessTest {
@ -142,8 +141,8 @@ public class EncryptDecryptTest extends AbstractPGPainlessTest {
PainlessResult encryptionResult = encryptor.getResult();
assertFalse(encryptionResult.getAllSignatureKeyIds().isEmpty());
for (long keyId : encryptionResult.getAllSignatureKeyIds()) {
assertFalse(encryptionResult.getAllSignatureKeyFingerprints().isEmpty());
for (long keyId : encryptionResult.getAllSignatureKeyFingerprints()) {
assertTrue(BCUtil.keyRingContainsKeyWithId(sender, keyId));
}
@ -168,8 +167,7 @@ public class EncryptDecryptTest extends AbstractPGPainlessTest {
DecryptionStream decryptor = PGPainless.createDecryptor()
.onInputStream(envelopeIn)
.decryptWith(keyDecryptor, BCUtil.keyRingsToKeyRingCollection(recipient))
.verifyWith(Collections.singleton(senderPub.getPublicKey().getKeyID()),
BCUtil.keyRingsToKeyRingCollection(senderPub))
.verifyWith(BCUtil.keyRingsToKeyRingCollection(senderPub))
.ignoreMissingPublicKeys()
.build();

View file

@ -0,0 +1,69 @@
/*
* Copyright 2018 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pgpainless.pgpainless;
import static junit.framework.TestCase.assertEquals;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.junit.Test;
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
public class OpenPgpV4FingerprintTest {
@Test(expected = PGPException.class)
public void fpTooShort() throws PGPException {
String fp = "484f57414c495645"; // Asking Mark
new OpenPgpV4Fingerprint(fp);
}
@Test(expected = PGPException.class)
public void invalidHexTest() throws PGPException {
String fp = "UNFORTUNATELYTHISISNOVALIDHEXADECIMALDOH";
new OpenPgpV4Fingerprint(fp);
}
@Test
public void validFingerprintTest() throws PGPException {
String fp = "4A4F48414E4E53454E2049532041204E45524421";
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint(fp);
assertEquals(fp, finger.toString());
}
@Test
public void convertsToUpperCaseTest() throws PGPException {
String fp = "444f4e5420552048415645204120484f4242593f";
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint(fp);
assertEquals("444F4E5420552048415645204120484F4242593F", finger.toString());
}
@Test
public void equalsOtherFingerprintTest() throws PGPException {
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint("5448452043414b452049532041204c4945212121");
assertEquals(finger, new OpenPgpV4Fingerprint("5448452043414B452049532041204C4945212121"));
}
@Test
public void keyIdTest() throws IOException, PGPException {
PGPPublicKey key = TestKeys.getJulietPublicKeyRing().getPublicKey();
long keyId = key.getKeyID();
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(key);
assertEquals(keyId, fingerprint.getKeyId());
}
}

View file

@ -57,9 +57,7 @@ public class TestKeysTest extends AbstractPGPainlessTest {
DecryptionStream decryptor = PGPainless.createDecryptor()
.onInputStream(new ByteArrayInputStream(encryptedMessage.getBytes()))
.decryptWith(new UnprotectedKeysProtector(), new PGPSecretKeyRingCollection(Collections.singleton(juliet)))
.verifyWith(
Collections.singleton(juliet.getPublicKey().getKeyID()),
BCUtil.keyRingsToKeyRingCollection(BCUtil.publicKeyRingFromSecretKeyRing(juliet)))
.verifyWith(BCUtil.keyRingsToKeyRingCollection(BCUtil.publicKeyRingFromSecretKeyRing(juliet)))
.ignoreMissingPublicKeys()
.build();