Added PGPKeyRingCollection. Added tests to KeyRingReaderTest to cover parsing of private/pub keys combinations.

This commit is contained in:
DenBond7 2021-04-09 14:24:07 +03:00 committed by Paul Schaub
parent 8e569e7931
commit 43647f3145
3 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,94 @@
package org.pgpainless.key.collection;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import javax.annotation.Nonnull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* This class describes a logic of handling a collection of different {@link PGPKeyRing}. The logic was inspired by
* {@link PGPSecretKeyRingCollection} and {@link PGPPublicKeyRingCollection}.
*/
public class PGPKeyRingCollection {
private PGPSecretKeyRingCollection pgpSecretKeyRingCollection;
private PGPPublicKeyRingCollection pgpPublicKeyRingCollection;
public PGPKeyRingCollection(@Nonnull byte[] encoding, @Nonnull KeyFingerPrintCalculator fingerPrintCalculator,
boolean isSilent) throws IOException, PGPException {
this(new ByteArrayInputStream(encoding), fingerPrintCalculator, isSilent);
}
/**
* Build a {@link PGPKeyRingCollection} from the passed in input stream.
*
* @param in input stream containing data
* @param isSilent flag indicating that unsupported objects will be ignored
* @throws IOException if a problem parsing the base stream occurs
* @throws PGPException if an object is encountered which isn't a {@link PGPSecretKeyRing} or {@link PGPPublicKeyRing}
*/
public PGPKeyRingCollection(@Nonnull InputStream in, @Nonnull KeyFingerPrintCalculator fingerPrintCalculator,
boolean isSilent) throws IOException, PGPException {
PGPObjectFactory pgpFact = new PGPObjectFactory(in, fingerPrintCalculator);
Object obj;
List<PGPSecretKeyRing> secretKeyRings = new ArrayList<>();
List<PGPPublicKeyRing> publicKeyRings = new ArrayList<>();
while ((obj = pgpFact.nextObject()) != null) {
if (obj instanceof PGPSecretKeyRing) {
secretKeyRings.add((PGPSecretKeyRing) obj);
} else if (obj instanceof PGPPublicKeyRing) {
publicKeyRings.add((PGPPublicKeyRing) obj);
} else if (!isSilent) {
throw new PGPException(obj.getClass().getName() + " found where " +
PGPSecretKeyRing.class.getSimpleName() + " or " +
PGPPublicKeyRing.class.getSimpleName() + " expected");
}
}
pgpSecretKeyRingCollection = new PGPSecretKeyRingCollection(secretKeyRings);
pgpPublicKeyRingCollection = new PGPPublicKeyRingCollection(publicKeyRings);
}
public PGPKeyRingCollection(Collection<PGPKeyRing> collection, boolean isSilent) throws IOException, PGPException {
List<PGPSecretKeyRing> secretKeyRings = new ArrayList<>();
List<PGPPublicKeyRing> publicKeyRings = new ArrayList<>();
for (PGPKeyRing pgpKeyRing : collection) {
if (pgpKeyRing instanceof PGPSecretKeyRing) {
secretKeyRings.add((PGPSecretKeyRing) pgpKeyRing);
} else if (pgpKeyRing instanceof PGPPublicKeyRing) {
publicKeyRings.add((PGPPublicKeyRing) pgpKeyRing);
} else if (!isSilent) {
throw new PGPException(pgpKeyRing.getClass().getName() + " found where " +
PGPSecretKeyRing.class.getSimpleName() + " or " +
PGPPublicKeyRing.class.getSimpleName() + " expected");
}
}
pgpSecretKeyRingCollection = new PGPSecretKeyRingCollection(secretKeyRings);
pgpPublicKeyRingCollection = new PGPPublicKeyRingCollection(publicKeyRings);
}
public PGPSecretKeyRingCollection getPGPSecretKeyRingCollection() {
return pgpSecretKeyRingCollection;
}
public PGPPublicKeyRingCollection getPgpPublicKeyRingCollection() {
return pgpPublicKeyRingCollection;
}
/**
* Return the number of rings in this collection.
*
* @return total size of {@link PGPSecretKeyRingCollection} and {@link PGPPublicKeyRingCollection}
* in this collection
*/
public int size() {
return pgpSecretKeyRingCollection.size() + pgpPublicKeyRingCollection.size();
}
}

View File

@ -29,6 +29,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.collection.PGPKeyRingCollection;
public class KeyRingReader {
@ -84,6 +85,19 @@ public class KeyRingReader {
return secretKeyRingCollection(asciiArmored.getBytes(UTF8));
}
public PGPKeyRingCollection keyRingCollection(@Nonnull InputStream inputStream, boolean isSilent)
throws IOException, PGPException {
return readKeyRingCollection(inputStream, isSilent);
}
public PGPKeyRingCollection keyRingCollection(@Nonnull byte[] bytes, boolean isSilent) throws IOException, PGPException {
return keyRingCollection(new ByteArrayInputStream(bytes), isSilent);
}
public PGPKeyRingCollection keyRingCollection(@Nonnull String asciiArmored, boolean isSilent) throws IOException, PGPException {
return keyRingCollection(asciiArmored.getBytes(UTF8), isSilent);
}
/*
STATIC METHODS
*/
@ -114,6 +128,14 @@ public class KeyRingReader {
ImplementationFactory.getInstance().getKeyFingerprintCalculator());
}
public static PGPKeyRingCollection readKeyRingCollection(@Nonnull InputStream inputStream, boolean isSilent)
throws IOException, PGPException {
return new PGPKeyRingCollection(
getDecoderStream(inputStream),
ImplementationFactory.getInstance().getKeyFingerprintCalculator(),
isSilent);
}
private static void validateStreamsNotBothNull(InputStream publicIn, InputStream secretIn) {
if (publicIn == null && secretIn == null) {
throw new NullPointerException("publicIn and secretIn cannot be BOTH null.");

View File

@ -31,6 +31,7 @@ import org.bouncycastle.openpgp.*;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.collection.PGPKeyRingCollection;
import org.pgpainless.key.util.KeyRingUtils;
class KeyRingReaderTest {
@ -189,12 +190,42 @@ class KeyRingReaderTest {
assertEquals(10, getPgpSecretKeyRingsFromResource("10_prv_keys_binary.key").size());
}
/**
* Many armored keys(private or pub) where each has own -----BEGIN PGP ... KEY BLOCK-----...-----END PGP ... KEY BLOCK-----
*/
@Test
void parseKeysMultiplyArmoredOwnHeader() throws IOException, PGPException, URISyntaxException {
assertEquals(10, getPGPKeyRingsFromResource("10_prv_and_pub_keys_armored_own_header.asc").size());
}
/**
* Many armored keys(private or pub) where each has own -----BEGIN PGP ... KEY BLOCK-----...-----END PGP ... KEY BLOCK-----
* Each of those blocks can have a different count of keys.
*/
@Test
void parseKeysMultiplyArmoredOwnWithSingleHeader() throws IOException, PGPException, URISyntaxException {
assertEquals(10, getPGPKeyRingsFromResource("10_prv_and_pub_keys_armored_own_with_single_header.asc").size());
}
/**
* Many binary keys(private or pub)
*/
@Test
void parseKeysMultiplyBinary() throws IOException, PGPException, URISyntaxException {
assertEquals(10, getPGPKeyRingsFromResource("10_prv_and_pub_keys_binary.key").size());
}
private InputStream getFileInputStreamFromResource(String fileName) throws IOException, URISyntaxException {
URL resource = getClass().getClassLoader().getResource(fileName);
assert resource != null;
return new FileInputStream(new File(resource.toURI()));
}
private PGPKeyRingCollection getPGPKeyRingsFromResource(String fileName)
throws IOException, URISyntaxException, PGPException {
return PGPainless.readKeyRing().keyRingCollection(getFileInputStreamFromResource(fileName), true);
}
private PGPPublicKeyRingCollection getPgpPublicKeyRingsFromResource(String fileName)
throws IOException, URISyntaxException, PGPException {
return PGPainless.readKeyRing().publicKeyRingCollection(getFileInputStreamFromResource(fileName));