mirror of
https://codeberg.org/PGPainless/cert-d-java.git
synced 2025-09-09 03:09:39 +02:00
Reintroduce pgp-certificate-store layer
This commit is contained in:
parent
f91c5065fc
commit
a3162f0cf9
25 changed files with 330 additions and 49 deletions
|
@ -26,6 +26,8 @@ dependencies {
|
|||
// Logging
|
||||
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
|
||||
api project(":pgp-certificate-store")
|
||||
|
||||
// SQL Subkey table
|
||||
testImplementation project(":pgp-cert-d-java-jdbc-sqlite-lookup")
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ package pgp.cert_d;
|
|||
|
||||
import pgp.cert_d.backend.FileBasedCertificateDirectoryBackend;
|
||||
import pgp.cert_d.backend.InMemoryCertificateDirectoryBackend;
|
||||
import pgp.cert_d.exception.NotAStoreException;
|
||||
import pgp.certificate.KeyMaterialReaderBackend;
|
||||
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
|
||||
import pgp.cert_d.subkey_lookup.SubkeyLookup;
|
||||
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
||||
import pgp.certificate_store.exception.NotAStoreException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
@ -18,18 +20,18 @@ public final class PGPCertificateDirectories {
|
|||
}
|
||||
|
||||
public static PGPCertificateDirectory inMemoryCertificateDirectory(KeyMaterialReaderBackend keyReader) {
|
||||
return new PGPCertificateDirectory(new InMemoryCertificateDirectoryBackend(keyReader));
|
||||
return new PGPCertificateDirectory(new InMemoryCertificateDirectoryBackend(keyReader), new InMemorySubkeyLookup());
|
||||
}
|
||||
|
||||
public static PGPCertificateDirectory defaultFileBasedCertificateDirectory(KeyMaterialReaderBackend keyReader)
|
||||
public static PGPCertificateDirectory defaultFileBasedCertificateDirectory(KeyMaterialReaderBackend keyReader, SubkeyLookup subkeyLookup)
|
||||
throws NotAStoreException {
|
||||
return fileBasedCertificateDirectory(keyReader, BaseDirectoryProvider.getDefaultBaseDir());
|
||||
return fileBasedCertificateDirectory(keyReader, BaseDirectoryProvider.getDefaultBaseDir(), subkeyLookup);
|
||||
}
|
||||
|
||||
public static PGPCertificateDirectory fileBasedCertificateDirectory(
|
||||
KeyMaterialReaderBackend keyReader, File baseDirectory)
|
||||
KeyMaterialReaderBackend keyReader, File baseDirectory, SubkeyLookup subkeyLookup)
|
||||
throws NotAStoreException {
|
||||
return new PGPCertificateDirectory(
|
||||
new FileBasedCertificateDirectoryBackend(baseDirectory, keyReader));
|
||||
new FileBasedCertificateDirectoryBackend(baseDirectory, keyReader), subkeyLookup);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,23 +4,28 @@
|
|||
|
||||
package pgp.cert_d;
|
||||
|
||||
import pgp.cert_d.exception.BadDataException;
|
||||
import pgp.cert_d.exception.BadNameException;
|
||||
import pgp.certificate.Certificate;
|
||||
import pgp.certificate.KeyMaterial;
|
||||
import pgp.certificate.KeyMaterialMerger;
|
||||
import pgp.cert_d.subkey_lookup.SubkeyLookup;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.KeyMaterial;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PGPCertificateDirectory
|
||||
implements ReadOnlyPGPCertificateDirectory, WritingPGPCertificateDirectory {
|
||||
implements ReadOnlyPGPCertificateDirectory, WritingPGPCertificateDirectory, SubkeyLookup {
|
||||
|
||||
private final Backend backend;
|
||||
private final SubkeyLookup subkeyLookup;
|
||||
|
||||
public PGPCertificateDirectory(Backend backend) {
|
||||
public PGPCertificateDirectory(Backend backend, SubkeyLookup subkeyLookup) {
|
||||
this.backend = backend;
|
||||
this.subkeyLookup = subkeyLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,6 +88,7 @@ public class PGPCertificateDirectory
|
|||
throws IOException, BadDataException, InterruptedException {
|
||||
backend.getLock().lockDirectory();
|
||||
KeyMaterial inserted = backend.doInsertTrustRoot(data, merge);
|
||||
subkeyLookup.storeCertificateSubkeyIds(inserted.getFingerprint(), inserted.getSubkeyIds());
|
||||
backend.getLock().releaseDirectory();
|
||||
return inserted;
|
||||
}
|
||||
|
@ -94,6 +100,7 @@ public class PGPCertificateDirectory
|
|||
return null;
|
||||
}
|
||||
KeyMaterial inserted = backend.doInsertTrustRoot(data, merge);
|
||||
subkeyLookup.storeCertificateSubkeyIds(inserted.getFingerprint(), inserted.getSubkeyIds());
|
||||
backend.getLock().releaseDirectory();
|
||||
return inserted;
|
||||
}
|
||||
|
@ -105,6 +112,7 @@ public class PGPCertificateDirectory
|
|||
throws IOException, BadDataException, InterruptedException {
|
||||
backend.getLock().lockDirectory();
|
||||
Certificate inserted = backend.doInsert(data, merge);
|
||||
subkeyLookup.storeCertificateSubkeyIds(inserted.getFingerprint(), inserted.getSubkeyIds());
|
||||
backend.getLock().releaseDirectory();
|
||||
return inserted;
|
||||
}
|
||||
|
@ -116,6 +124,7 @@ public class PGPCertificateDirectory
|
|||
return null;
|
||||
}
|
||||
Certificate inserted = backend.doInsert(data, merge);
|
||||
subkeyLookup.storeCertificateSubkeyIds(inserted.getFingerprint(), inserted.getSubkeyIds());
|
||||
backend.getLock().releaseDirectory();
|
||||
return inserted;
|
||||
}
|
||||
|
@ -125,6 +134,7 @@ public class PGPCertificateDirectory
|
|||
throws IOException, BadDataException, BadNameException, InterruptedException {
|
||||
backend.getLock().lockDirectory();
|
||||
Certificate inserted = backend.doInsertWithSpecialName(specialName, data, merge);
|
||||
subkeyLookup.storeCertificateSubkeyIds(inserted.getFingerprint(), inserted.getSubkeyIds());
|
||||
backend.getLock().releaseDirectory();
|
||||
return inserted;
|
||||
}
|
||||
|
@ -136,10 +146,21 @@ public class PGPCertificateDirectory
|
|||
return null;
|
||||
}
|
||||
Certificate inserted = backend.doInsertWithSpecialName(specialName, data, merge);
|
||||
subkeyLookup.storeCertificateSubkeyIds(inserted.getFingerprint(), inserted.getSubkeyIds());
|
||||
backend.getLock().releaseDirectory();
|
||||
return inserted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCertificateFingerprintsForSubkeyId(long subkeyId) throws IOException {
|
||||
return subkeyLookup.getCertificateFingerprintsForSubkeyId(subkeyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeCertificateSubkeyIds(String certificate, List<Long> subkeyIds) throws IOException {
|
||||
subkeyLookup.storeCertificateSubkeyIds(certificate, subkeyIds);
|
||||
}
|
||||
|
||||
public interface Backend {
|
||||
|
||||
LockingMechanism getLock();
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d;
|
||||
|
||||
import pgp.certificate_store.PGPCertificateStore;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Adapter class to adapt a {@link PGPCertificateDirectory} to the {@link PGPCertificateStore} interface.
|
||||
*/
|
||||
public class PGPCertificateStoreAdapter implements PGPCertificateStore {
|
||||
|
||||
private final PGPCertificateDirectory directory;
|
||||
|
||||
public PGPCertificateStoreAdapter(PGPCertificateDirectory directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate getCertificate(String identifier)
|
||||
throws IOException, BadNameException, BadDataException {
|
||||
if (SpecialNames.lookupSpecialName(identifier) != null) {
|
||||
return directory.getBySpecialName(identifier);
|
||||
} else {
|
||||
return directory.getByFingerprint(identifier.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Certificate> getCertificatesBySubkeyId(long subkeyId)
|
||||
throws IOException, BadDataException {
|
||||
Set<String> fingerprints = directory.getCertificateFingerprintsForSubkeyId(subkeyId);
|
||||
Set<Certificate> certificates = new HashSet<>();
|
||||
for (String fingerprint : fingerprints) {
|
||||
try {
|
||||
certificates.add(directory.getByFingerprint(fingerprint));
|
||||
} catch (BadNameException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return certificates.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate insertCertificate(InputStream data, KeyMaterialMerger merge)
|
||||
throws IOException, InterruptedException, BadDataException {
|
||||
Certificate certificate = directory.insert(data, merge);
|
||||
return certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate insertCertificateBySpecialName(String specialName, InputStream data, KeyMaterialMerger merge)
|
||||
throws IOException, InterruptedException, BadDataException, BadNameException {
|
||||
return directory.insertWithSpecialName(specialName, data, merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Certificate> getCertificates() {
|
||||
return directory.items();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getFingerprints() {
|
||||
return directory.fingerprints();
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
package pgp.cert_d;
|
||||
|
||||
import pgp.cert_d.exception.BadDataException;
|
||||
import pgp.cert_d.exception.BadNameException;
|
||||
import pgp.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
package pgp.cert_d;
|
||||
|
||||
import pgp.cert_d.exception.BadDataException;
|
||||
import pgp.cert_d.exception.BadNameException;
|
||||
import pgp.certificate.Certificate;
|
||||
import pgp.certificate.KeyMaterial;
|
||||
import pgp.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.KeyMaterial;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
|
|
@ -6,13 +6,13 @@ package pgp.cert_d.backend;
|
|||
|
||||
import pgp.cert_d.PGPCertificateDirectory;
|
||||
import pgp.cert_d.SpecialNames;
|
||||
import pgp.cert_d.exception.BadDataException;
|
||||
import pgp.cert_d.exception.BadNameException;
|
||||
import pgp.cert_d.exception.NotAStoreException;
|
||||
import pgp.certificate.Certificate;
|
||||
import pgp.certificate.KeyMaterial;
|
||||
import pgp.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate.KeyMaterialReaderBackend;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.KeyMaterial;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
import pgp.certificate_store.exception.NotAStoreException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
|
|
|
@ -6,12 +6,12 @@ package pgp.cert_d.backend;
|
|||
|
||||
import pgp.cert_d.PGPCertificateDirectory;
|
||||
import pgp.cert_d.SpecialNames;
|
||||
import pgp.cert_d.exception.BadDataException;
|
||||
import pgp.cert_d.exception.BadNameException;
|
||||
import pgp.certificate.Certificate;
|
||||
import pgp.certificate.KeyMaterial;
|
||||
import pgp.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate.KeyMaterialReaderBackend;
|
||||
import pgp.certificate_store.certificate.Certificate;
|
||||
import pgp.certificate_store.certificate.KeyMaterial;
|
||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
||||
import pgp.certificate_store.exception.BadDataException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.exception;
|
||||
|
||||
/**
|
||||
* The data was not a valid OpenPGP cert or key in binary format.
|
||||
*/
|
||||
public class BadDataException extends Exception {
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.exception;
|
||||
|
||||
/**
|
||||
* Provided name was neither a valid fingerprint, nor a known special name.
|
||||
*/
|
||||
public class BadNameException extends Exception {
|
||||
|
||||
public BadNameException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BadNameException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.exception;
|
||||
|
||||
/**
|
||||
* The base dir cannot possibly contain a store.
|
||||
*/
|
||||
public class NotAStoreException extends Exception {
|
||||
|
||||
public NotAStoreException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NotAStoreException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Exceptions.
|
||||
*/
|
||||
package pgp.cert_d.exception;
|
|
@ -1,16 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.certificate;
|
||||
|
||||
/**
|
||||
* OpenPGP certificate (public key).
|
||||
*/
|
||||
public abstract class Certificate implements KeyMaterial {
|
||||
|
||||
@Override
|
||||
public Certificate asCertificate() {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.certificate;
|
||||
|
||||
/**
|
||||
* OpenPGP key (secret key).
|
||||
*/
|
||||
public abstract class Key implements KeyMaterial {
|
||||
|
||||
/**
|
||||
* Return the certificate part of this OpenPGP key.
|
||||
*
|
||||
* @return OpenPGP certificate
|
||||
*/
|
||||
public abstract Certificate getCertificate();
|
||||
|
||||
@Override
|
||||
public Certificate asCertificate() {
|
||||
return getCertificate();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.certificate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
|
||||
public interface KeyMaterial {
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the certificate as 40 lowercase hex characters.
|
||||
* TODO: Allow OpenPGP V5 fingerprints
|
||||
*
|
||||
* @return fingerprint
|
||||
*/
|
||||
String getFingerprint();
|
||||
|
||||
Certificate asCertificate();
|
||||
|
||||
/**
|
||||
* Return an {@link InputStream} of the binary representation of the secret key.
|
||||
*
|
||||
* @return input stream
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
String getTag() throws IOException;
|
||||
|
||||
/**
|
||||
* Return a {@link Set} containing key-ids of subkeys.
|
||||
*
|
||||
* @return subkeys
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
Set<Long> getSubkeyIds() throws IOException;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.certificate;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Merge a given {@link Key} (update) with an existing {@link Key}.
|
||||
*/
|
||||
public interface KeyMaterialMerger {
|
||||
|
||||
/**
|
||||
* Merge the given key material with an existing copy and return the result.
|
||||
* If no existing {@link KeyMaterial} is found (i.e. if existing is null), this method returns the unmodified data.
|
||||
*
|
||||
* @param data key material
|
||||
* @param existing optional already existing copy of the key material
|
||||
* @return merged key material
|
||||
*
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
KeyMaterial merge(KeyMaterial data, KeyMaterial existing) throws IOException;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.certificate;
|
||||
|
||||
import pgp.cert_d.exception.BadDataException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface KeyMaterialReaderBackend {
|
||||
|
||||
/**
|
||||
* Read a {@link KeyMaterial} (either {@link Key} or {@link Certificate}) from the given {@link InputStream}.
|
||||
*
|
||||
* @param data input stream containing the binary representation of the key.
|
||||
* @return key or certificate object
|
||||
*
|
||||
* @throws IOException in case of an IO error
|
||||
* @throws BadDataException in case that the data stream does not contain a valid OpenPGP key/certificate
|
||||
*/
|
||||
KeyMaterial read(InputStream data) throws IOException, BadDataException;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* General OpenPGP Certificate Storage related classes.
|
||||
*/
|
||||
package pgp.certificate;
|
|
@ -7,7 +7,7 @@ package pgp.cert_d;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import pgp.cert_d.backend.FileBasedCertificateDirectoryBackend;
|
||||
import pgp.cert_d.exception.BadNameException;
|
||||
import pgp.certificate_store.exception.BadNameException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue