mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-07-01 07:46:43 +02:00
Add SharedPGPCertificateDirectoryAdapter class
This commit is contained in:
parent
d086332677
commit
8fb677676f
8
pgp-cert-d-java/README.md
Normal file
8
pgp-cert-d-java/README.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Paul Schaub <info@pgpainless.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Shared PGP Certificate Directory for Java
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.cert_d;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import pgp.cert_d.exception.BadDataException;
|
||||||
|
import pgp.cert_d.exception.BadNameException;
|
||||||
|
import pgp.certificate_store.Certificate;
|
||||||
|
import pgp.certificate_store.CertificateStore;
|
||||||
|
import pgp.certificate_store.MergeCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter class used to adapt the {@link SharedPGPCertificateDirectory} for use with
|
||||||
|
* {@link CertificateStore}.
|
||||||
|
*/
|
||||||
|
public class SharedPGPCertificateDirectoryAdapter
|
||||||
|
implements CertificateStore {
|
||||||
|
|
||||||
|
private final SharedPGPCertificateDirectory directory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an adapter to use {@link SharedPGPCertificateDirectory} objects as {@link CertificateStore CertificateStores}.
|
||||||
|
*
|
||||||
|
* @param directory directory instance
|
||||||
|
*/
|
||||||
|
public SharedPGPCertificateDirectoryAdapter(SharedPGPCertificateDirectory directory) {
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate getCertificate(String identifier)
|
||||||
|
throws IOException {
|
||||||
|
SpecialName specialName = SpecialName.fromString(identifier);
|
||||||
|
if (specialName != null) {
|
||||||
|
try {
|
||||||
|
return directory.get(specialName);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new IllegalArgumentException("Unknown special name " + identifier, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return directory.get(identifier);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid fingerprint or unknown special name " + identifier, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate getCertificateIfChanged(String identifier, String tag)
|
||||||
|
throws IOException {
|
||||||
|
SpecialName specialName = SpecialName.fromString(identifier);
|
||||||
|
if (specialName != null) {
|
||||||
|
try {
|
||||||
|
return directory.getIfChanged(specialName, tag);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new IllegalArgumentException("Unknown special name " + identifier, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return directory.getIfChanged(identifier, tag);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid fingerprint or unknown special name " + identifier, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate insertCertificate(InputStream data, MergeCallback merge)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
try {
|
||||||
|
return directory.insert(data, merge);
|
||||||
|
} catch (BadDataException e) {
|
||||||
|
throw new IOException("Cannot insert certificate due to bad data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate tryInsertCertificate(InputStream data, MergeCallback merge)
|
||||||
|
throws IOException {
|
||||||
|
try {
|
||||||
|
return directory.tryInsert(data, merge);
|
||||||
|
} catch (BadDataException e) {
|
||||||
|
throw new IOException("Cannot insert certificate due to bad data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate insertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
try {
|
||||||
|
SpecialName specialNameEnum = SpecialName.fromString(specialName);
|
||||||
|
if (specialNameEnum == null) {
|
||||||
|
throw new IllegalArgumentException("Unknown special name " + specialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return directory.insertSpecial(specialNameEnum, data, merge);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new IllegalArgumentException("Unknown special name " + specialName);
|
||||||
|
} catch (BadDataException e) {
|
||||||
|
throw new IOException("Cannot insert certificate due to bad data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate tryInsertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge)
|
||||||
|
throws IOException {
|
||||||
|
try {
|
||||||
|
SpecialName specialNameEnum = SpecialName.fromString(specialName);
|
||||||
|
if (specialNameEnum == null) {
|
||||||
|
throw new IllegalArgumentException("Unknown special name " + specialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return directory.tryInsertSpecial(specialNameEnum, data, merge);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new IllegalArgumentException("Unknown special name " + specialName);
|
||||||
|
} catch (BadDataException e) {
|
||||||
|
throw new IOException("Cannot insert certificate due to bad data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Certificate> getCertificates() {
|
||||||
|
return directory.items();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<String> getFingerprints() {
|
||||||
|
return directory.fingerprints();
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import pgp.cert_d.exception.BadNameException;
|
||||||
import pgp.cert_d.exception.NotAStoreException;
|
import pgp.cert_d.exception.NotAStoreException;
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
import pgp.certificate_store.MergeCallback;
|
import pgp.certificate_store.MergeCallback;
|
||||||
import pgp.certificate_store.ParserBackend;
|
import pgp.certificate_store.CertificateReaderBackend;
|
||||||
|
|
||||||
public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDirectory {
|
public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDirectory {
|
||||||
|
|
||||||
|
@ -29,16 +29,16 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
private final Pattern openPgpV4FingerprintPattern = Pattern.compile("^[a-f0-9]{40}$");
|
private final Pattern openPgpV4FingerprintPattern = Pattern.compile("^[a-f0-9]{40}$");
|
||||||
|
|
||||||
private final LockingMechanism writeLock;
|
private final LockingMechanism writeLock;
|
||||||
private final ParserBackend parserBackend;
|
private final CertificateReaderBackend certificateReaderBackend;
|
||||||
|
|
||||||
public SharedPGPCertificateDirectoryImpl(ParserBackend parserBackend)
|
public SharedPGPCertificateDirectoryImpl(CertificateReaderBackend certificateReaderBackend)
|
||||||
throws NotAStoreException {
|
throws NotAStoreException {
|
||||||
this(OSUtil.getDefaultBaseDir(), parserBackend);
|
this(OSUtil.getDefaultBaseDir(), certificateReaderBackend);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedPGPCertificateDirectoryImpl(File baseDirectory, ParserBackend parserBackend)
|
public SharedPGPCertificateDirectoryImpl(File baseDirectory, CertificateReaderBackend certificateReaderBackend)
|
||||||
throws NotAStoreException {
|
throws NotAStoreException {
|
||||||
this.parserBackend = parserBackend;
|
this.certificateReaderBackend = certificateReaderBackend;
|
||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
if (!baseDirectory.exists()) {
|
if (!baseDirectory.exists()) {
|
||||||
if (!baseDirectory.mkdirs()) {
|
if (!baseDirectory.mkdirs()) {
|
||||||
|
@ -83,7 +83,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
}
|
}
|
||||||
FileInputStream fileIn = new FileInputStream(certFile);
|
FileInputStream fileIn = new FileInputStream(certFile);
|
||||||
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
||||||
Certificate certificate = parserBackend.readCertificate(bufferedIn);
|
Certificate certificate = certificateReaderBackend.readCertificate(bufferedIn);
|
||||||
|
|
||||||
if (!certificate.getFingerprint().equals(fingerprint)) {
|
if (!certificate.getFingerprint().equals(fingerprint)) {
|
||||||
// TODO: Figure out more suitable exception
|
// TODO: Figure out more suitable exception
|
||||||
|
@ -102,7 +102,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
|
|
||||||
FileInputStream fileIn = new FileInputStream(certFile);
|
FileInputStream fileIn = new FileInputStream(certFile);
|
||||||
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
||||||
Certificate certificate = parserBackend.readCertificate(bufferedIn);
|
Certificate certificate = certificateReaderBackend.readCertificate(bufferedIn);
|
||||||
|
|
||||||
return certificate;
|
return certificate;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
}
|
}
|
||||||
|
|
||||||
private Certificate _insert(InputStream data, MergeCallback merge) throws IOException, BadDataException {
|
private Certificate _insert(InputStream data, MergeCallback merge) throws IOException, BadDataException {
|
||||||
Certificate newCertificate = parserBackend.readCertificate(data);
|
Certificate newCertificate = certificateReaderBackend.readCertificate(data);
|
||||||
Certificate existingCertificate;
|
Certificate existingCertificate;
|
||||||
File certFile;
|
File certFile;
|
||||||
try {
|
try {
|
||||||
|
@ -209,7 +209,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
}
|
}
|
||||||
|
|
||||||
private Certificate _insertSpecial(SpecialName specialName, InputStream data, MergeCallback merge) throws IOException, BadNameException, BadDataException {
|
private Certificate _insertSpecial(SpecialName specialName, InputStream data, MergeCallback merge) throws IOException, BadNameException, BadDataException {
|
||||||
Certificate newCertificate = parserBackend.readCertificate(data);
|
Certificate newCertificate = certificateReaderBackend.readCertificate(data);
|
||||||
Certificate existingCertificate = get(specialName);
|
Certificate existingCertificate = get(specialName);
|
||||||
File certFile = getCertFile(specialName);
|
File certFile = getCertFile(specialName);
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
@Override
|
@Override
|
||||||
Certificate get() {
|
Certificate get() {
|
||||||
try {
|
try {
|
||||||
return parserBackend.readCertificate(new FileInputStream(certFile));
|
return certificateReaderBackend.readCertificate(new FileInputStream(certFile));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError("File got deleted.");
|
throw new AssertionError("File got deleted.");
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ public class SharedPGPCertificateDirectoryImpl implements SharedPGPCertificateDi
|
||||||
@Override
|
@Override
|
||||||
Certificate get() throws BadDataException {
|
Certificate get() throws BadDataException {
|
||||||
try {
|
try {
|
||||||
Certificate certificate = parserBackend.readCertificate(new FileInputStream(certFile));
|
Certificate certificate = certificateReaderBackend.readCertificate(new FileInputStream(certFile));
|
||||||
if (!(subdirectory.getName() + certFile.getName()).equals(certificate.getFingerprint())) {
|
if (!(subdirectory.getName() + certFile.getName()).equals(certificate.getFingerprint())) {
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
}
|
}
|
||||||
|
|
10
pgp-certificate-store/README.md
Normal file
10
pgp-certificate-store/README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Paul Schaub <info@pgpainless.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# PGP Certificate Store Definitions
|
||||||
|
|
||||||
|
This module contains API definitions for a certificate store for PGPainless.
|
||||||
|
A certificate store is used to store public key certificates only.
|
|
@ -0,0 +1,26 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.certificate_store;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface definition for a class that can read {@link Certificate Certificates} from binary
|
||||||
|
* {@link InputStream InputStreams}.
|
||||||
|
*/
|
||||||
|
public interface CertificateReaderBackend {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a {@link Certificate} from the given {@link InputStream}.
|
||||||
|
*
|
||||||
|
* @param inputStream input stream containing the binary representation of the certificate.
|
||||||
|
* @return certificate object
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an IO error
|
||||||
|
*/
|
||||||
|
Certificate readCertificate(InputStream inputStream) throws IOException;
|
||||||
|
|
||||||
|
}
|
|
@ -8,21 +8,46 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certificate storage definition.
|
||||||
|
* This interface defines methods to insert and retrieve {@link Certificate Certificates} to and from a store.
|
||||||
|
*
|
||||||
|
* {@link Certificate Certificates} are hereby identified by identifiers. An identifier can either be a fingerprint
|
||||||
|
* or a special name. Special names are implementation-defined identifiers for certificates.
|
||||||
|
*
|
||||||
|
* Fingerprints are expected to be hexadecimal lowercase character sequences.
|
||||||
|
*/
|
||||||
public interface CertificateStore {
|
public interface CertificateStore {
|
||||||
|
|
||||||
Certificate get(String identifier) throws IOException;
|
/**
|
||||||
|
* Return the certificate that matches the given identifier.
|
||||||
|
* If no matching certificate can be found, return null.
|
||||||
|
*
|
||||||
|
* @param identifier identifier for a certificate.
|
||||||
|
* @return certificate or null
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an IO-error
|
||||||
|
*/
|
||||||
|
Certificate getCertificate(String identifier) throws IOException;
|
||||||
|
|
||||||
Certificate getIfChanged(String identifier, String tag) throws IOException;
|
/**
|
||||||
|
*
|
||||||
|
* @param identifier
|
||||||
|
* @param tag
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
Certificate getCertificateIfChanged(String identifier, String tag) throws IOException;
|
||||||
|
|
||||||
Certificate insert(InputStream data, MergeCallback merge) throws IOException;
|
Certificate insertCertificate(InputStream data, MergeCallback merge) throws IOException, InterruptedException;
|
||||||
|
|
||||||
Certificate tryInsert(InputStream data, MergeCallback merge) throws IOException;
|
Certificate tryInsertCertificate(InputStream data, MergeCallback merge) throws IOException;
|
||||||
|
|
||||||
Certificate insertSpecial(String specialName, InputStream data, MergeCallback merge) throws IOException;
|
Certificate insertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge) throws IOException, InterruptedException;
|
||||||
|
|
||||||
Certificate tryInsertSpecial(String specialName, InputStream data, MergeCallback merge) throws IOException;
|
Certificate tryInsertCertificateBySpecialName(String specialName, InputStream data, MergeCallback merge) throws IOException;
|
||||||
|
|
||||||
Iterator<Certificate> items();
|
Iterator<Certificate> getCertificates();
|
||||||
|
|
||||||
Iterator<String> fingerprints();
|
Iterator<String> getFingerprints();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package pgp.certificate_store;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface ParserBackend {
|
|
||||||
|
|
||||||
Certificate readCertificate(InputStream inputStream) throws IOException;
|
|
||||||
|
|
||||||
}
|
|
7
pgpainless-cert-d/README.md
Normal file
7
pgpainless-cert-d/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Paul Schaub <info@pgpainless.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Certificate Stores + PGPainless
|
|
@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.platform.commons.logging.Logger;
|
import org.junit.platform.commons.logging.Logger;
|
||||||
import org.junit.platform.commons.logging.LoggerFactory;
|
import org.junit.platform.commons.logging.LoggerFactory;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.certificate_store.CertificateParser;
|
import org.pgpainless.certificate_store.CertificateCertificateReader;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
import pgp.cert_d.FileLockingMechanism;
|
import pgp.cert_d.FileLockingMechanism;
|
||||||
import pgp.cert_d.LockingMechanism;
|
import pgp.cert_d.LockingMechanism;
|
||||||
|
@ -51,7 +51,7 @@ public class SharedPGPCertificateDirectoryTest {
|
||||||
public void beforeEach() throws IOException, NotAStoreException {
|
public void beforeEach() throws IOException, NotAStoreException {
|
||||||
File tempDir = Files.createTempDirectory("pgp.cert.d-").toFile();
|
File tempDir = Files.createTempDirectory("pgp.cert.d-").toFile();
|
||||||
tempDir.deleteOnExit();
|
tempDir.deleteOnExit();
|
||||||
directory = new SharedPGPCertificateDirectoryImpl(tempDir, new CertificateParser());
|
directory = new SharedPGPCertificateDirectoryImpl(tempDir, new CertificateCertificateReader());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -15,9 +15,9 @@ import org.bouncycastle.util.encoders.Base64;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
import pgp.certificate_store.ParserBackend;
|
import pgp.certificate_store.CertificateReaderBackend;
|
||||||
|
|
||||||
public class CertificateParser implements ParserBackend {
|
public class CertificateCertificateReader implements CertificateReaderBackend {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate readCertificate(InputStream inputStream) throws IOException {
|
public Certificate readCertificate(InputStream inputStream) throws IOException {
|
Loading…
Reference in a new issue