diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad2475e..f2bfcca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ SPDX-License-Identifier: CC0-1.0
# Cert-D-Java Changelog
+## 0.2.2
+- Bump Bouncy Castle to `1.75`
+- Bump `sqlite-jdbc` to `3.42.0.0`
+
+## 0.2.1
+- Throw `NoSuchElementException` when querying non-existent certificates
+
## 0.2.0
- `pgp-certificate-store`:
- Rework `Certificate`, `Key` to inherit from `KeyMaterial`
diff --git a/README.md b/README.md
index fb3cb90..4dde81f 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ SPDX-License-Identifier: Apache-2.0
This repository contains a number of modules defining OpenPGP certificate storage for Java and Android applications.
-The module [pgp-certificate-store](pgp-certificate-store] defines generalized
+The module [pgp-certificate-store](pgp-certificate-store) defines generalized
interfaces for OpenPGP Certificate storage.
It can be used by applications and libraries such as
[PGPainless](https://pgpainless.org/) for certificate management.
diff --git a/pgp-cert-d-java/build.gradle b/pgp-cert-d-java/build.gradle
index d59867d..19f1329 100644
--- a/pgp-cert-d-java/build.gradle
+++ b/pgp-cert-d-java/build.gradle
@@ -32,7 +32,7 @@ dependencies {
testImplementation project(":pgp-cert-d-java-jdbc-sqlite-lookup")
testImplementation "org.bouncycastle:bcprov-jdk15to18:$bouncycastleVersion"
- testImplementation "org.bouncycastle:bcpg-jdk15to18:$bouncycastleVersion"
+ testImplementation "org.bouncycastle:bcpg-jdk15to18:$bouncyPgVersion"
}
animalsniffer {
diff --git a/pgp-cert-d-java/src/main/java/pgp/cert_d/PGPCertificateDirectory.java b/pgp-cert-d-java/src/main/java/pgp/cert_d/PGPCertificateDirectory.java
index 1d54212..87d2512 100644
--- a/pgp-cert-d-java/src/main/java/pgp/cert_d/PGPCertificateDirectory.java
+++ b/pgp-cert-d-java/src/main/java/pgp/cert_d/PGPCertificateDirectory.java
@@ -15,6 +15,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -46,13 +48,17 @@ public class PGPCertificateDirectory
if (!openPgpV4FingerprintPattern.matcher(fingerprint).matches()) {
throw new BadNameException();
}
- return backend.readByFingerprint(fingerprint);
+ Certificate certificate = backend.readByFingerprint(fingerprint);
+ if (certificate == null) {
+ throw new NoSuchElementException();
+ }
+ return certificate;
}
@Override
public Certificate getByFingerprintIfChanged(String fingerprint, long tag)
throws IOException, BadNameException, BadDataException {
- if (tag != backend.getTagForFingerprint(fingerprint)) {
+ if (!Objects.equals(tag, backend.getTagForFingerprint(fingerprint))) {
return getByFingerprint(fingerprint);
}
return null;
@@ -66,13 +72,13 @@ public class PGPCertificateDirectory
if (keyMaterial != null) {
return keyMaterial.asCertificate();
}
- return null;
+ throw new NoSuchElementException();
}
@Override
public Certificate getBySpecialNameIfChanged(String specialName, long tag)
throws IOException, BadNameException, BadDataException {
- if (tag != backend.getTagForSpecialName(specialName)) {
+ if (!Objects.equals(tag, backend.getTagForSpecialName(specialName))) {
return getBySpecialName(specialName);
}
return null;
@@ -121,7 +127,11 @@ public class PGPCertificateDirectory
@Override
public KeyMaterial getTrustRoot() throws IOException, BadDataException {
try {
- return backend.readBySpecialName(SpecialNames.TRUST_ROOT);
+ KeyMaterial keyMaterial = backend.readBySpecialName(SpecialNames.TRUST_ROOT);
+ if (keyMaterial == null) {
+ throw new NoSuchElementException();
+ }
+ return keyMaterial;
} catch (BadNameException e) {
throw new AssertionError("'" + SpecialNames.TRUST_ROOT + "' is implementation MUST");
}
diff --git a/pgp-cert-d-java/src/main/java/pgp/cert_d/ReadOnlyPGPCertificateDirectory.java b/pgp-cert-d-java/src/main/java/pgp/cert_d/ReadOnlyPGPCertificateDirectory.java
index 0b1416c..1b27ffb 100644
--- a/pgp-cert-d-java/src/main/java/pgp/cert_d/ReadOnlyPGPCertificateDirectory.java
+++ b/pgp-cert-d-java/src/main/java/pgp/cert_d/ReadOnlyPGPCertificateDirectory.java
@@ -10,6 +10,7 @@ import pgp.certificate_store.exception.BadNameException;
import java.io.IOException;
import java.util.Iterator;
+import java.util.NoSuchElementException;
/**
* Interface for a read-only OpenPGP certificate directory.
@@ -19,12 +20,12 @@ public interface ReadOnlyPGPCertificateDirectory {
/**
* Get the trust-root certificate. This is a certificate which is stored under the special name
*
trust-root
.
- * If no such certificate is found, null
is returned.
*
* @return trust-root certificate
*
* @throws IOException in case of an IO error
* @throws BadDataException if the certificate contains bad data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getTrustRootCertificate()
throws IOException, BadDataException;
@@ -36,24 +37,25 @@ public interface ReadOnlyPGPCertificateDirectory {
* Otherwise. the changed certificate is returned.
*
* @param tag tag
- * @return changed certificate, or null if the certificate is unchanged or not found.
+ * @return changed certificate, or null if the certificate is unchanged.
*
* @throws IOException in case of an IO error
* @throws BadDataException if the certificate contains bad data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getTrustRootCertificateIfChanged(long tag)
throws IOException, BadDataException;
/**
* Get the certificate identified by the given fingerprint.
- * If no such certificate is found, return null
.
*
* @param fingerprint lower-case fingerprint of the certificate
- * @return certificate or null if no such certificate has been found
+ * @return certificate
*
* @throws IOException in case of an IO error
* @throws BadNameException if the fingerprint is malformed
* @throws BadDataException if the certificate contains bad data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getByFingerprint(String fingerprint)
throws IOException, BadNameException, BadDataException;
@@ -66,25 +68,26 @@ public interface ReadOnlyPGPCertificateDirectory {
*
* @param fingerprint lower-case fingerprint of the certificate
* @param tag tag
- * @return certificate or null if the certificate has not been changed or has not been found
+ * @return certificate or null if the certificate has not been changed
*
* @throws IOException in case of an IO error
* @throws BadNameException if the fingerprint is malformed
* @throws BadDataException if the certificate contains bad data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getByFingerprintIfChanged(String fingerprint, long tag)
throws IOException, BadNameException, BadDataException;
/**
* Get the certificate identified by the given special name.
- * If no such certificate is found, null
is returned.
*
* @param specialName special name
- * @return certificate or null
+ * @return certificate
*
* @throws IOException in case of an IO error
* @throws BadNameException if the special name is not known
* @throws BadDataException if the certificate contains bad data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getBySpecialName(String specialName)
throws IOException, BadNameException, BadDataException;
@@ -102,6 +105,7 @@ public interface ReadOnlyPGPCertificateDirectory {
* @throws IOException in case of an IO error
* @throws BadNameException if the special name is not known
* @throws BadDataException if the certificate contains bad data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getBySpecialNameIfChanged(String specialName, long tag)
throws IOException, BadNameException, BadDataException;
diff --git a/pgp-cert-d-java/src/main/java/pgp/cert_d/backend/FileBasedCertificateDirectoryBackend.java b/pgp-cert-d-java/src/main/java/pgp/cert_d/backend/FileBasedCertificateDirectoryBackend.java
index 89e52ba..08ac356 100644
--- a/pgp-cert-d-java/src/main/java/pgp/cert_d/backend/FileBasedCertificateDirectoryBackend.java
+++ b/pgp-cert-d-java/src/main/java/pgp/cert_d/backend/FileBasedCertificateDirectoryBackend.java
@@ -33,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.regex.Pattern;
/**
@@ -348,7 +349,7 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
private Long getTag(File file) throws IOException {
if (!file.exists()) {
- throw new IllegalArgumentException("File MUST exist.");
+ throw new NoSuchElementException();
}
Path path = file.toPath();
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
diff --git a/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java b/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java
index 357ba3f..f6af1be 100644
--- a/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java
+++ b/pgp-cert-d-java/src/test/java/pgp/cert_d/BaseDirectoryProviderTest.java
@@ -18,7 +18,7 @@ public class BaseDirectoryProviderTest {
public void testGetDefaultBaseDir_Linux() {
assumeTrue(System.getProperty("os.name").equalsIgnoreCase("linux"));
File baseDir = BaseDirectoryProvider.getDefaultBaseDirForOS("linux");
- assertTrue(baseDir.getAbsolutePath().endsWith("/.local/share/pgp.cert.d"));
+ assertTrue(baseDir.getAbsolutePath().endsWith("pgp.cert.d"));
}
@Test
diff --git a/pgp-cert-d-java/src/test/java/pgp/cert_d/FileBasedPGPCertificateDirectoryTest.java b/pgp-cert-d-java/src/test/java/pgp/cert_d/FileBasedPGPCertificateDirectoryTest.java
new file mode 100644
index 0000000..8701e33
--- /dev/null
+++ b/pgp-cert-d-java/src/test/java/pgp/cert_d/FileBasedPGPCertificateDirectoryTest.java
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: 2022 Paul Schaub
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package pgp.cert_d;
+
+import org.bouncycastle.util.io.Streams;
+import org.junit.jupiter.api.Test;
+import pgp.cert_d.backend.FileBasedCertificateDirectoryBackend;
+import pgp.cert_d.dummy.TestKeyMaterialMerger;
+import pgp.cert_d.dummy.TestKeyMaterialReaderBackend;
+import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
+import pgp.cert_d.subkey_lookup.SubkeyLookup;
+import pgp.certificate_store.certificate.Certificate;
+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.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+public class FileBasedPGPCertificateDirectoryTest {
+
+ private static final KeyMaterialMerger merger = new TestKeyMaterialMerger();
+ @Test
+ public void testFileBasedCertificateDirectoryTagChangesWhenFileChanges()
+ throws IOException, NotAStoreException, BadDataException, InterruptedException, BadNameException {
+ File tempDir = Files.createTempDirectory("file-based-changes").toFile();
+ tempDir.deleteOnExit();
+ PGPCertificateDirectory directory = PGPCertificateDirectories.fileBasedCertificateDirectory(
+ new TestKeyMaterialReaderBackend(),
+ tempDir,
+ new InMemorySubkeyLookup());
+ FileBasedCertificateDirectoryBackend.FilenameResolver resolver =
+ new FileBasedCertificateDirectoryBackend.FilenameResolver(tempDir);
+
+ // Insert certificate
+ Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
+ Long tag = certificate.getTag();
+ assertNotNull(tag);
+ assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
+
+ Long oldTag = tag;
+
+ Thread.sleep(10);
+ // Change the file on disk directly, this invalidates the tag due to changed modification date
+ File certFile = resolver.getCertFileByFingerprint(certificate.getFingerprint());
+ FileOutputStream fileOut = new FileOutputStream(certFile);
+ Streams.pipeAll(certificate.getInputStream(), fileOut);
+ fileOut.write("\n".getBytes());
+ fileOut.close();
+
+ // Old invalidated tag indicates a change, so the modified certificate is returned
+ certificate = directory.getByFingerprintIfChanged(certificate.getFingerprint(), oldTag);
+ assertNotNull(certificate);
+
+ // new tag is valid
+ tag = certificate.getTag();
+ assertNotEquals(oldTag, tag);
+ assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
+ }
+
+ @Test
+ public void fileBasedStoreInWriteProtectedAreaThrows() {
+ File root = new File("/");
+ assumeTrue(root.exists(), "This test only runs on unix-like systems");
+ File baseDirectory = new File(root, "pgp.cert.d");
+ assumeFalse(baseDirectory.mkdirs(), "This test assumes that we cannot create dirs in /");
+
+ KeyMaterialReaderBackend reader = new TestKeyMaterialReaderBackend();
+ SubkeyLookup lookup = new InMemorySubkeyLookup();
+ assertThrows(NotAStoreException.class, () -> PGPCertificateDirectories.fileBasedCertificateDirectory(
+ reader, baseDirectory, lookup));
+ }
+
+ @Test
+ public void fileBasedStoreOnFileThrows()
+ throws IOException {
+ File tempDir = Files.createTempDirectory("containsAFile").toFile();
+ tempDir.deleteOnExit();
+ File baseDir = new File(tempDir, "pgp.cert.d");
+ baseDir.createNewFile(); // this is a file, not a dir
+
+ KeyMaterialReaderBackend reader = new TestKeyMaterialReaderBackend();
+ SubkeyLookup lookup = new InMemorySubkeyLookup();
+ assertThrows(NotAStoreException.class, () -> PGPCertificateDirectories.fileBasedCertificateDirectory(
+ reader, baseDir, lookup));
+ }
+
+ @Test
+ public void testCertificateStoredUnderWrongFingerprintThrowsBadData()
+ throws IOException, NotAStoreException, BadDataException, InterruptedException, BadNameException {
+ File tempDir = Files.createTempDirectory("wrong-fingerprint").toFile();
+ tempDir.deleteOnExit();
+ PGPCertificateDirectory directory = PGPCertificateDirectories.fileBasedCertificateDirectory(
+ new TestKeyMaterialReaderBackend(),
+ tempDir,
+ new InMemorySubkeyLookup());
+ FileBasedCertificateDirectoryBackend.FilenameResolver resolver =
+ new FileBasedCertificateDirectoryBackend.FilenameResolver(tempDir);
+
+ // Insert Rons certificate
+ directory.insert(TestKeys.getRonCert(), merger);
+
+ // Copy Rons cert to Cedrics cert file
+ File ronCert = resolver.getCertFileByFingerprint(TestKeys.RON_FP);
+ FileInputStream inputStream = new FileInputStream(ronCert);
+ File cedricCert = resolver.getCertFileByFingerprint(TestKeys.CEDRIC_FP);
+ cedricCert.getParentFile().mkdirs();
+ cedricCert.createNewFile();
+ FileOutputStream outputStream = new FileOutputStream(cedricCert);
+ Streams.pipeAll(inputStream, outputStream);
+ inputStream.close();
+ outputStream.close();
+
+ // Reading cedrics cert will fail, as it has Rons fingerprint
+ assertThrows(BadDataException.class, () -> directory.getByFingerprint(TestKeys.CEDRIC_FP));
+ }
+}
diff --git a/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateDirectoryTest.java b/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateDirectoryTest.java
index 4ffb865..f6bd858 100644
--- a/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateDirectoryTest.java
+++ b/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateDirectoryTest.java
@@ -6,11 +6,9 @@ package pgp.cert_d;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Named;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import pgp.cert_d.backend.FileBasedCertificateDirectoryBackend;
import pgp.cert_d.dummy.TestKeyMaterialMerger;
import pgp.cert_d.dummy.TestKeyMaterialReaderBackend;
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
@@ -24,13 +22,13 @@ import pgp.certificate_store.exception.NotAStoreException;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Stream;
@@ -68,6 +66,55 @@ public class PGPCertificateDirectoryTest {
Arguments.of(Named.of("FileBasedCertificateDirectory", fileBased)));
}
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentCertByFingerprintThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getByFingerprint("0000000000000000000000000000000000000000"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentCertByFingerprintIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getByFingerprintIfChanged("0000000000000000000000000000000000000000", 12));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentCertBySpecialNameThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getBySpecialName(SpecialNames.TRUST_ROOT));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentCertBySpecialNameIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, 12));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentTrustRootThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getTrustRoot());
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentTrustRootIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getTrustRootCertificateIfChanged(12));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void getNonExistentTrustRootCertificateThrowsNoSuchElementException(PGPCertificateDirectory directory) {
+ assertThrows(NoSuchElementException.class, () ->
+ directory.getTrustRootCertificate());
+ }
+
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void lockDirectoryAndTryInsertWillFail(PGPCertificateDirectory directory)
@@ -130,7 +177,7 @@ public class PGPCertificateDirectoryTest {
@MethodSource("provideTestSubjects")
public void testInsertAndGetSingleCert(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException, BadNameException {
- assertNull(directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate");
+ assertThrows(NoSuchElementException.class, () -> directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate");
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
@@ -148,7 +195,7 @@ public class PGPCertificateDirectoryTest {
@MethodSource("provideTestSubjects")
public void testInsertAndGetTrustRootAndCert(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException {
- assertNull(directory.getTrustRoot());
+ assertThrows(NoSuchElementException.class, () -> directory.getTrustRoot());
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger);
@@ -188,6 +235,7 @@ public class PGPCertificateDirectoryTest {
assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1));
Long oldTag = tag;
+ Thread.sleep(10);
// "update" key
trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger);
@@ -222,38 +270,42 @@ public class PGPCertificateDirectoryTest {
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag + 1));
}
- @Test
- public void testFileBasedCertificateDirectoryTagChangesWhenFileChanges() throws IOException, NotAStoreException, BadDataException, InterruptedException, BadNameException {
- File tempDir = Files.createTempDirectory("file-based-changes").toFile();
- tempDir.deleteOnExit();
- PGPCertificateDirectory directory = PGPCertificateDirectories.fileBasedCertificateDirectory(
- new TestKeyMaterialReaderBackend(),
- tempDir,
- new InMemorySubkeyLookup());
- FileBasedCertificateDirectoryBackend.FilenameResolver resolver =
- new FileBasedCertificateDirectoryBackend.FilenameResolver(tempDir);
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void testOverwriteTrustRoot(PGPCertificateDirectory directory)
+ throws BadDataException, IOException, InterruptedException {
+ directory.insertTrustRoot(TestKeys.getHarryKey(), merger);
+ assertEquals(HARRY_FP, directory.getTrustRoot().getFingerprint());
+ assertTrue(directory.getTrustRoot() instanceof Key);
- // Insert certificate
- Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
- Long tag = certificate.getTag();
- assertNotNull(tag);
- assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
-
- Long oldTag = tag;
-
- // Change the file on disk directly, this invalidates the tag due to changed modification date
- File certFile = resolver.getCertFileByFingerprint(certificate.getFingerprint());
- FileOutputStream fileOut = new FileOutputStream(certFile);
- Streams.pipeAll(certificate.getInputStream(), fileOut);
- fileOut.close();
-
- // Old invalidated tag indicates a change, so the modified certificate is returned
- certificate = directory.getByFingerprintIfChanged(certificate.getFingerprint(), oldTag);
- assertNotNull(certificate);
-
- // new tag is valid
- tag = certificate.getTag();
- assertNotEquals(oldTag, tag);
- assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
+ directory.insertTrustRoot(TestKeys.getCedricCert(), merger);
+ assertEquals(CEDRIC_FP, directory.getTrustRoot().getFingerprint());
+ assertTrue(directory.getTrustRoot() instanceof Certificate);
}
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void testOverwriteSpecialName(PGPCertificateDirectory directory)
+ throws BadDataException, IOException, InterruptedException, BadNameException {
+ directory.insertWithSpecialName(SpecialNames.TRUST_ROOT, TestKeys.getRonCert(), merger);
+ KeyMaterial bySpecialName = directory.getBySpecialName(SpecialNames.TRUST_ROOT);
+ assertEquals(RON_FP, bySpecialName.getFingerprint());
+
+ directory.insertWithSpecialName(SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
+ assertEquals(HARRY_FP, directory.getBySpecialName(SpecialNames.TRUST_ROOT).getFingerprint());
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTestSubjects")
+ public void testOverwriteByFingerprint(PGPCertificateDirectory directory)
+ throws BadDataException, IOException, InterruptedException, BadNameException {
+ directory.insert(TestKeys.getRonCert(), merger);
+ Certificate extracted = directory.getByFingerprint(RON_FP);
+ assertEquals(RON_FP, extracted.getFingerprint());
+
+ directory.insert(TestKeys.getRonCert(), merger);
+ extracted = directory.getByFingerprint(RON_FP);
+ assertEquals(RON_FP, extracted.getFingerprint());
+ }
+
}
diff --git a/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateStoreAdapterTest.java b/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateStoreAdapterTest.java
index 56fdac6..79b5fed 100644
--- a/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateStoreAdapterTest.java
+++ b/pgp-cert-d-java/src/test/java/pgp/cert_d/PGPCertificateStoreAdapterTest.java
@@ -17,6 +17,7 @@ import pgp.certificate_store.exception.BadNameException;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -52,7 +53,7 @@ public class PGPCertificateStoreAdapterTest {
@Test
public void testInsertGetCertificate()
throws BadDataException, IOException, InterruptedException, BadNameException {
- assertNull(adapter.getCertificate(TestKeys.CEDRIC_FP));
+ assertThrows(NoSuchElementException.class, () -> adapter.getCertificate(TestKeys.CEDRIC_FP));
assertFalse(adapter.getCertificates().hasNext());
Certificate certificate = adapter.insertCertificate(TestKeys.getCedricCert(), merger);
@@ -70,7 +71,7 @@ public class PGPCertificateStoreAdapterTest {
@Test
public void testInsertGetTrustRoot()
throws BadDataException, BadNameException, IOException, InterruptedException {
- assertNull(adapter.getCertificate(SpecialNames.TRUST_ROOT));
+ assertThrows(NoSuchElementException.class, () -> adapter.getCertificate(SpecialNames.TRUST_ROOT));
Certificate certificate = adapter.insertCertificateBySpecialName(
SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);
diff --git a/pgp-certificate-store/src/main/java/pgp/certificate_store/PGPCertificateStore.java b/pgp-certificate-store/src/main/java/pgp/certificate_store/PGPCertificateStore.java
index d0915b0..251229f 100644
--- a/pgp-certificate-store/src/main/java/pgp/certificate_store/PGPCertificateStore.java
+++ b/pgp-certificate-store/src/main/java/pgp/certificate_store/PGPCertificateStore.java
@@ -12,6 +12,7 @@ import pgp.certificate_store.exception.BadNameException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
+import java.util.NoSuchElementException;
/**
* Interface for an OpenPGP certificate (public key) store.
@@ -20,7 +21,6 @@ public interface PGPCertificateStore {
/**
* 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
@@ -28,6 +28,7 @@ public interface PGPCertificateStore {
* @throws IOException in case of an IO-error
* @throws BadNameException if the identifier is invalid
* @throws BadDataException if the certificate file contains invalid data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getCertificate(String identifier)
throws IOException, BadNameException, BadDataException;
@@ -45,6 +46,7 @@ public interface PGPCertificateStore {
* @throws IOException in case of an IO-error
* @throws BadNameException if the identifier is invalid
* @throws BadDataException if the certificate file contains invalid data
+ * @throws NoSuchElementException if no such certificate is found
*/
Certificate getCertificateIfChanged(String identifier, Long tag)
throws IOException, BadNameException, BadDataException;
diff --git a/version.gradle b/version.gradle
index 8e3b938..2903026 100644
--- a/version.gradle
+++ b/version.gradle
@@ -4,15 +4,16 @@
allprojects {
ext {
- shortVersion = '0.2.0'
- isSnapshot = false
+ shortVersion = '0.2.3'
+ isSnapshot = true
minAndroidSdk = 26
animalsnifferSignatureVersion = "$minAndroidSdk:8.0.0_r2"
javaSourceCompatibility = 1.8
- bouncycastleVersion = '1.71'
+ bouncycastleVersion = '1.75'
+ bouncyPgVersion = "$bouncycastleVersion"
slf4jVersion = '1.7.36'
logbackVersion = '1.2.11'
junitVersion = '5.8.2'
- sqliteJdbcVersion = '3.36.0.3'
+ sqliteJdbcVersion = '3.42.0.0'
}
}