mirror of
https://codeberg.org/PGPainless/cert-d-java.git
synced 2024-11-24 16:32:09 +01:00
Add back support for getXIfChanged(Y, tag)
This commit is contained in:
parent
d050cb5516
commit
27f4598437
15 changed files with 423 additions and 101 deletions
|
@ -16,7 +16,7 @@ apply plugin: 'ru.vyarus.animalsniffer'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// animal sniffer for ensuring Android API compatibility
|
// animal sniffer for ensuring Android API compatibility
|
||||||
signature "net.sf.androidscents.signature:android-api-level-${minAndroidSdk}:2.3.3_r2@signature"
|
signature "net.sf.androidscents.signature:android-api-level-${minAndroidSdk}:8.0.0_r2@signature"
|
||||||
|
|
||||||
// JUnit
|
// JUnit
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||||
|
|
|
@ -33,6 +33,16 @@ public class PGPCertificateDirectory
|
||||||
return backend.readByFingerprint(fingerprint);
|
return backend.readByFingerprint(fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate getByFingerprintIfChanged(String fingerprint, long tag)
|
||||||
|
throws IOException, BadNameException, BadDataException {
|
||||||
|
if (tag != backend.getTagForFingerprint(fingerprint)) {
|
||||||
|
return getByFingerprint(fingerprint);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate getBySpecialName(String specialName)
|
public Certificate getBySpecialName(String specialName)
|
||||||
throws BadNameException, BadDataException, IOException {
|
throws BadNameException, BadDataException, IOException {
|
||||||
|
@ -43,6 +53,15 @@ public class PGPCertificateDirectory
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate getBySpecialNameIfChanged(String specialName, long tag)
|
||||||
|
throws IOException, BadNameException, BadDataException {
|
||||||
|
if (tag != backend.getTagForSpecialName(specialName)) {
|
||||||
|
return getBySpecialName(specialName);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate getTrustRootCertificate()
|
public Certificate getTrustRootCertificate()
|
||||||
throws IOException, BadDataException {
|
throws IOException, BadDataException {
|
||||||
|
@ -53,6 +72,15 @@ public class PGPCertificateDirectory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate getTrustRootCertificateIfChanged(long tag) throws IOException, BadDataException {
|
||||||
|
try {
|
||||||
|
return getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag);
|
||||||
|
} catch (BadNameException e) {
|
||||||
|
throw new AssertionError("'" + SpecialNames.TRUST_ROOT + "' is an implementation MUST");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Certificate> items() {
|
public Iterator<Certificate> items() {
|
||||||
return backend.readItems();
|
return backend.readItems();
|
||||||
|
@ -179,6 +207,10 @@ public class PGPCertificateDirectory
|
||||||
|
|
||||||
Certificate doInsertWithSpecialName(String specialName, InputStream data, KeyMaterialMerger merge)
|
Certificate doInsertWithSpecialName(String specialName, InputStream data, KeyMaterialMerger merge)
|
||||||
throws IOException, BadDataException, BadNameException;
|
throws IOException, BadDataException, BadNameException;
|
||||||
|
|
||||||
|
Long getTagForFingerprint(String fingerprint) throws BadNameException, IOException;
|
||||||
|
|
||||||
|
Long getTagForSpecialName(String specialName) throws BadNameException, IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface LockingMechanism {
|
public interface LockingMechanism {
|
||||||
|
|
|
@ -37,6 +37,16 @@ public class PGPCertificateStoreAdapter implements PGPCertificateStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate getCertificateIfChanged(String identifier, Long tag)
|
||||||
|
throws IOException, BadNameException, BadDataException {
|
||||||
|
if (SpecialNames.lookupSpecialName(identifier) != null) {
|
||||||
|
return directory.getBySpecialNameIfChanged(identifier, tag);
|
||||||
|
} else {
|
||||||
|
return directory.getByFingerprintIfChanged(identifier.toLowerCase(), tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Certificate> getCertificatesBySubkeyId(long subkeyId)
|
public Iterator<Certificate> getCertificatesBySubkeyId(long subkeyId)
|
||||||
throws IOException, BadDataException {
|
throws IOException, BadDataException {
|
||||||
|
|
|
@ -16,12 +16,21 @@ public interface ReadOnlyPGPCertificateDirectory {
|
||||||
Certificate getTrustRootCertificate()
|
Certificate getTrustRootCertificate()
|
||||||
throws IOException, BadDataException;
|
throws IOException, BadDataException;
|
||||||
|
|
||||||
|
Certificate getTrustRootCertificateIfChanged(long tag)
|
||||||
|
throws IOException, BadDataException;
|
||||||
|
|
||||||
Certificate getByFingerprint(String fingerprint)
|
Certificate getByFingerprint(String fingerprint)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
|
Certificate getByFingerprintIfChanged(String fingerprint, long tag)
|
||||||
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
Certificate getBySpecialName(String specialName)
|
Certificate getBySpecialName(String specialName)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
|
Certificate getBySpecialNameIfChanged(String specialName, long tag)
|
||||||
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
Iterator<Certificate> items();
|
Iterator<Certificate> items();
|
||||||
|
|
||||||
Iterator<String> fingerprints();
|
Iterator<String> fingerprints();
|
||||||
|
|
|
@ -7,6 +7,7 @@ package pgp.cert_d.backend;
|
||||||
import pgp.cert_d.PGPCertificateDirectory;
|
import pgp.cert_d.PGPCertificateDirectory;
|
||||||
import pgp.cert_d.SpecialNames;
|
import pgp.cert_d.SpecialNames;
|
||||||
import pgp.certificate_store.certificate.Certificate;
|
import pgp.certificate_store.certificate.Certificate;
|
||||||
|
import pgp.certificate_store.certificate.Key;
|
||||||
import pgp.certificate_store.certificate.KeyMaterial;
|
import pgp.certificate_store.certificate.KeyMaterial;
|
||||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||||
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
||||||
|
@ -25,6 +26,9 @@ import java.io.InputStream;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.channels.FileLock;
|
import java.nio.channels.FileLock;
|
||||||
import java.nio.channels.OverlappingFileLockException;
|
import java.nio.channels.OverlappingFileLockException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -160,10 +164,12 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long tag = getTagForFingerprint(fingerprint);
|
||||||
|
|
||||||
FileInputStream fileIn = new FileInputStream(certFile);
|
FileInputStream fileIn = new FileInputStream(certFile);
|
||||||
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
||||||
|
|
||||||
Certificate certificate = reader.read(bufferedIn).asCertificate();
|
Certificate certificate = reader.read(bufferedIn, tag).asCertificate();
|
||||||
if (!certificate.getFingerprint().equals(fingerprint)) {
|
if (!certificate.getFingerprint().equals(fingerprint)) {
|
||||||
// TODO: Figure out more suitable exception
|
// TODO: Figure out more suitable exception
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
|
@ -179,9 +185,11 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long tag = getTagForSpecialName(specialName);
|
||||||
|
|
||||||
FileInputStream fileIn = new FileInputStream(certFile);
|
FileInputStream fileIn = new FileInputStream(certFile);
|
||||||
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
BufferedInputStream bufferedIn = new BufferedInputStream(fileIn);
|
||||||
KeyMaterial keyMaterial = reader.read(bufferedIn);
|
KeyMaterial keyMaterial = reader.read(bufferedIn, tag);
|
||||||
|
|
||||||
return keyMaterial;
|
return keyMaterial;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +222,8 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
@Override
|
@Override
|
||||||
Certificate get() throws BadDataException {
|
Certificate get() throws BadDataException {
|
||||||
try {
|
try {
|
||||||
Certificate certificate = reader.read(new FileInputStream(certFile)).asCertificate();
|
long tag = getTag(certFile);
|
||||||
|
Certificate certificate = reader.read(new FileInputStream(certFile), tag).asCertificate();
|
||||||
if (!(subdirectory.getName() + certFile.getName()).equals(certificate.getFingerprint())) {
|
if (!(subdirectory.getName() + certFile.getName()).equals(certificate.getFingerprint())) {
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
}
|
}
|
||||||
|
@ -246,7 +255,7 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyMaterial doInsertTrustRoot(InputStream data, KeyMaterialMerger merge) throws BadDataException, IOException {
|
public KeyMaterial doInsertTrustRoot(InputStream data, KeyMaterialMerger merge) throws BadDataException, IOException {
|
||||||
KeyMaterial newCertificate = reader.read(data);
|
KeyMaterial newCertificate = reader.read(data, null);
|
||||||
KeyMaterial existingCertificate;
|
KeyMaterial existingCertificate;
|
||||||
File certFile;
|
File certFile;
|
||||||
try {
|
try {
|
||||||
|
@ -256,18 +265,22 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingCertificate != null && !newCertificate.getTag().equals(existingCertificate.getTag())) {
|
if (existingCertificate != null) {
|
||||||
newCertificate = merge.merge(newCertificate, existingCertificate);
|
newCertificate = merge.merge(newCertificate, existingCertificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFile(newCertificate.getInputStream(), certFile);
|
long tag = writeToFile(newCertificate.getInputStream(), certFile);
|
||||||
|
if (newCertificate instanceof Key) {
|
||||||
|
newCertificate = new Key((Key) newCertificate, tag);
|
||||||
|
} else {
|
||||||
|
newCertificate = new Certificate((Certificate) newCertificate, tag);
|
||||||
|
}
|
||||||
return newCertificate;
|
return newCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate doInsert(InputStream data, KeyMaterialMerger merge) throws IOException, BadDataException {
|
public Certificate doInsert(InputStream data, KeyMaterialMerger merge) throws IOException, BadDataException {
|
||||||
KeyMaterial newCertificate = reader.read(data);
|
KeyMaterial newCertificate = reader.read(data, null);
|
||||||
Certificate existingCertificate;
|
Certificate existingCertificate;
|
||||||
File certFile;
|
File certFile;
|
||||||
try {
|
try {
|
||||||
|
@ -277,18 +290,17 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingCertificate != null && !newCertificate.getTag().equals(existingCertificate.getTag())) {
|
if (existingCertificate != null) {
|
||||||
newCertificate = merge.merge(newCertificate, existingCertificate);
|
newCertificate = merge.merge(newCertificate, existingCertificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFile(newCertificate.getInputStream(), certFile);
|
long tag = writeToFile(newCertificate.getInputStream(), certFile);
|
||||||
|
return new Certificate(newCertificate.asCertificate(), tag);
|
||||||
return newCertificate.asCertificate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate doInsertWithSpecialName(String specialName, InputStream data, KeyMaterialMerger merge) throws IOException, BadDataException, BadNameException {
|
public Certificate doInsertWithSpecialName(String specialName, InputStream data, KeyMaterialMerger merge) throws IOException, BadDataException, BadNameException {
|
||||||
KeyMaterial newCertificate = reader.read(data);
|
KeyMaterial newCertificate = reader.read(data, null);
|
||||||
KeyMaterial existingCertificate;
|
KeyMaterial existingCertificate;
|
||||||
File certFile;
|
File certFile;
|
||||||
try {
|
try {
|
||||||
|
@ -298,16 +310,41 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingCertificate != null && !newCertificate.getTag().equals(existingCertificate.getTag())) {
|
if (existingCertificate != null) {
|
||||||
newCertificate = merge.merge(newCertificate, existingCertificate);
|
newCertificate = merge.merge(newCertificate, existingCertificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFile(newCertificate.getInputStream(), certFile);
|
long tag = writeToFile(newCertificate.getInputStream(), certFile);
|
||||||
|
return new Certificate(newCertificate.asCertificate(), tag);
|
||||||
return newCertificate.asCertificate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeToFile(InputStream inputStream, File certFile)
|
@Override
|
||||||
|
public Long getTagForFingerprint(String fingerprint) throws BadNameException, IOException {
|
||||||
|
File file = resolver.getCertFileByFingerprint(fingerprint);
|
||||||
|
return getTag(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTagForSpecialName(String specialName) throws BadNameException, IOException {
|
||||||
|
File file = resolver.getCertFileBySpecialName(specialName);
|
||||||
|
return getTag(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long getTag(File file) throws IOException {
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new IllegalArgumentException("File MUST exist.");
|
||||||
|
}
|
||||||
|
Path path = file.toPath();
|
||||||
|
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||||
|
|
||||||
|
// On UNIX file systems, for example, fileKey() will return the device ID and inode
|
||||||
|
int fileId = attrs.fileKey().hashCode();
|
||||||
|
long lastMod = attrs.lastModifiedTime().toMillis();
|
||||||
|
|
||||||
|
return lastMod + (11L * fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long writeToFile(InputStream inputStream, File certFile)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
certFile.getParentFile().mkdirs();
|
certFile.getParentFile().mkdirs();
|
||||||
if (!certFile.exists() && !certFile.createNewFile()) {
|
if (!certFile.exists() && !certFile.createNewFile()) {
|
||||||
|
@ -324,6 +361,7 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
|
||||||
|
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
fileOut.close();
|
fileOut.close();
|
||||||
|
return getTag(certFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FilenameResolver {
|
public static class FilenameResolver {
|
||||||
|
|
|
@ -7,6 +7,7 @@ package pgp.cert_d.backend;
|
||||||
import pgp.cert_d.PGPCertificateDirectory;
|
import pgp.cert_d.PGPCertificateDirectory;
|
||||||
import pgp.cert_d.SpecialNames;
|
import pgp.cert_d.SpecialNames;
|
||||||
import pgp.certificate_store.certificate.Certificate;
|
import pgp.certificate_store.certificate.Certificate;
|
||||||
|
import pgp.certificate_store.certificate.Key;
|
||||||
import pgp.certificate_store.certificate.KeyMaterial;
|
import pgp.certificate_store.certificate.KeyMaterial;
|
||||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||||
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
import pgp.certificate_store.certificate.KeyMaterialReaderBackend;
|
||||||
|
@ -91,7 +92,7 @@ public class InMemoryCertificateDirectoryBackend implements PGPCertificateDirect
|
||||||
@Override
|
@Override
|
||||||
public KeyMaterial doInsertTrustRoot(InputStream data, KeyMaterialMerger merge)
|
public KeyMaterial doInsertTrustRoot(InputStream data, KeyMaterialMerger merge)
|
||||||
throws BadDataException, IOException {
|
throws BadDataException, IOException {
|
||||||
KeyMaterial update = reader.read(data);
|
KeyMaterial update = reader.read(data, null);
|
||||||
KeyMaterial existing = null;
|
KeyMaterial existing = null;
|
||||||
try {
|
try {
|
||||||
existing = readBySpecialName(SpecialNames.TRUST_ROOT);
|
existing = readBySpecialName(SpecialNames.TRUST_ROOT);
|
||||||
|
@ -100,6 +101,11 @@ public class InMemoryCertificateDirectoryBackend implements PGPCertificateDirect
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
KeyMaterial merged = merge.merge(update, existing);
|
KeyMaterial merged = merge.merge(update, existing);
|
||||||
|
if (merged instanceof Key) {
|
||||||
|
merged = new Key((Key) merged, System.currentTimeMillis());
|
||||||
|
} else {
|
||||||
|
merged = new Certificate((Certificate) merged, System.currentTimeMillis());
|
||||||
|
}
|
||||||
keyMaterialSpecialNameMap.put(SpecialNames.TRUST_ROOT, merged);
|
keyMaterialSpecialNameMap.put(SpecialNames.TRUST_ROOT, merged);
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
@ -108,9 +114,10 @@ public class InMemoryCertificateDirectoryBackend implements PGPCertificateDirect
|
||||||
@Override
|
@Override
|
||||||
public Certificate doInsert(InputStream data, KeyMaterialMerger merge)
|
public Certificate doInsert(InputStream data, KeyMaterialMerger merge)
|
||||||
throws IOException, BadDataException {
|
throws IOException, BadDataException {
|
||||||
KeyMaterial update = reader.read(data);
|
KeyMaterial update = reader.read(data, null);
|
||||||
Certificate existing = readByFingerprint(update.getFingerprint());
|
Certificate existing = readByFingerprint(update.getFingerprint());
|
||||||
Certificate merged = merge.merge(update, existing).asCertificate();
|
Certificate merged = merge.merge(update, existing).asCertificate();
|
||||||
|
merged = new Certificate(merged, System.currentTimeMillis());
|
||||||
certificateFingerprintMap.put(update.getFingerprint(), merged);
|
certificateFingerprintMap.put(update.getFingerprint(), merged);
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
@ -118,10 +125,36 @@ public class InMemoryCertificateDirectoryBackend implements PGPCertificateDirect
|
||||||
@Override
|
@Override
|
||||||
public Certificate doInsertWithSpecialName(String specialName, InputStream data, KeyMaterialMerger merge)
|
public Certificate doInsertWithSpecialName(String specialName, InputStream data, KeyMaterialMerger merge)
|
||||||
throws IOException, BadDataException, BadNameException {
|
throws IOException, BadDataException, BadNameException {
|
||||||
KeyMaterial keyMaterial = reader.read(data);
|
KeyMaterial keyMaterial = reader.read(data, null);
|
||||||
KeyMaterial existing = readBySpecialName(specialName);
|
KeyMaterial existing = readBySpecialName(specialName);
|
||||||
KeyMaterial merged = merge.merge(keyMaterial, existing);
|
KeyMaterial merged = merge.merge(keyMaterial, existing);
|
||||||
|
if (merged instanceof Key) {
|
||||||
|
merged = new Key((Key) merged, System.currentTimeMillis());
|
||||||
|
} else {
|
||||||
|
merged = new Certificate((Certificate) merged, System.currentTimeMillis());
|
||||||
|
}
|
||||||
keyMaterialSpecialNameMap.put(specialName, merged);
|
keyMaterialSpecialNameMap.put(specialName, merged);
|
||||||
return merged.asCertificate();
|
return merged.asCertificate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTagForFingerprint(String fingerprint) throws BadNameException, IOException {
|
||||||
|
Certificate certificate = certificateFingerprintMap.get(fingerprint);
|
||||||
|
if (certificate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return certificate.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTagForSpecialName(String specialName) throws BadNameException, IOException {
|
||||||
|
if (SpecialNames.lookupSpecialName(specialName) == null) {
|
||||||
|
throw new BadNameException("Invalid special name " + specialName);
|
||||||
|
}
|
||||||
|
KeyMaterial tagged = keyMaterialSpecialNameMap.get(specialName);
|
||||||
|
if (tagged == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return tagged.getTag();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,17 @@
|
||||||
package pgp.cert_d;
|
package pgp.cert_d;
|
||||||
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
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;
|
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookup;
|
||||||
import pgp.certificate_store.certificate.Certificate;
|
import pgp.certificate_store.certificate.Certificate;
|
||||||
import pgp.certificate_store.certificate.Key;
|
import pgp.certificate_store.certificate.Key;
|
||||||
import pgp.certificate_store.certificate.KeyMaterial;
|
import pgp.certificate_store.certificate.KeyMaterial;
|
||||||
|
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
||||||
import pgp.certificate_store.exception.BadDataException;
|
import pgp.certificate_store.exception.BadDataException;
|
||||||
import pgp.certificate_store.exception.BadNameException;
|
import pgp.certificate_store.exception.BadNameException;
|
||||||
import pgp.certificate_store.exception.NotAStoreException;
|
import pgp.certificate_store.exception.NotAStoreException;
|
||||||
|
@ -18,6 +23,7 @@ import pgp.certificate_store.exception.NotAStoreException;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
@ -30,6 +36,7 @@ import java.util.stream.Stream;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
@ -37,6 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class PGPCertificateDirectoryTest {
|
public class PGPCertificateDirectoryTest {
|
||||||
|
|
||||||
|
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||||
private static final Charset UTF8 = Charset.forName("UTF8");
|
private static final Charset UTF8 = Charset.forName("UTF8");
|
||||||
|
|
||||||
private static final String HARRY_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
private static final String HARRY_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
@ -143,7 +151,10 @@ public class PGPCertificateDirectoryTest {
|
||||||
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||||
private static final String CEDRIC_FP = "5e75bf20646bc1a98d3b1bc2fe9cd472987c4021";
|
private static final String CEDRIC_FP = "5e75bf20646bc1a98d3b1bc2fe9cd472987c4021";
|
||||||
|
|
||||||
private static Stream<PGPCertificateDirectory> provideTestSubjects() throws IOException, NotAStoreException {
|
private static final KeyMaterialMerger merger = new TestKeyMaterialMerger();
|
||||||
|
|
||||||
|
private static Stream<PGPCertificateDirectory> provideTestSubjects()
|
||||||
|
throws IOException, NotAStoreException {
|
||||||
PGPCertificateDirectory inMemory = PGPCertificateDirectories.inMemoryCertificateDirectory(
|
PGPCertificateDirectory inMemory = PGPCertificateDirectories.inMemoryCertificateDirectory(
|
||||||
new TestKeyMaterialReaderBackend());
|
new TestKeyMaterialReaderBackend());
|
||||||
|
|
||||||
|
@ -159,18 +170,19 @@ public class PGPCertificateDirectoryTest {
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("provideTestSubjects")
|
@MethodSource("provideTestSubjects")
|
||||||
public void lockDirectoryAndInsertWillFail(PGPCertificateDirectory directory) throws IOException, InterruptedException, BadDataException {
|
public void lockDirectoryAndInsertWillFail(PGPCertificateDirectory directory)
|
||||||
|
throws IOException, InterruptedException, BadDataException {
|
||||||
// Manually lock the dir
|
// Manually lock the dir
|
||||||
assertFalse(directory.backend.getLock().isLocked());
|
assertFalse(directory.backend.getLock().isLocked());
|
||||||
directory.backend.getLock().lockDirectory();
|
directory.backend.getLock().lockDirectory();
|
||||||
assertTrue(directory.backend.getLock().isLocked());
|
assertTrue(directory.backend.getLock().isLocked());
|
||||||
assertFalse(directory.backend.getLock().tryLockDirectory());
|
assertFalse(directory.backend.getLock().tryLockDirectory());
|
||||||
|
|
||||||
Certificate inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
Certificate inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||||
assertNull(inserted);
|
assertNull(inserted);
|
||||||
|
|
||||||
directory.backend.getLock().releaseDirectory();
|
directory.backend.getLock().releaseDirectory();
|
||||||
inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
inserted = directory.tryInsert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||||
assertNotNull(inserted);
|
assertNotNull(inserted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +200,7 @@ public class PGPCertificateDirectoryTest {
|
||||||
|
|
||||||
ByteArrayInputStream bytesIn = new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8));
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8));
|
||||||
|
|
||||||
Certificate certificate = directory.insert(bytesIn, new TestKeyMaterialMerger());
|
Certificate certificate = directory.insert(bytesIn, merger);
|
||||||
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
|
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
|
||||||
|
|
||||||
Certificate get = directory.getByFingerprint(CEDRIC_FP);
|
Certificate get = directory.getByFingerprint(CEDRIC_FP);
|
||||||
|
@ -207,7 +219,7 @@ public class PGPCertificateDirectoryTest {
|
||||||
assertNull(directory.getTrustRoot());
|
assertNull(directory.getTrustRoot());
|
||||||
|
|
||||||
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
||||||
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), new TestKeyMaterialMerger());
|
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||||
assertNotNull(trustRootMaterial);
|
assertNotNull(trustRootMaterial);
|
||||||
assertTrue(trustRootMaterial instanceof Key);
|
assertTrue(trustRootMaterial instanceof Key);
|
||||||
assertEquals(HARRY_FP, trustRootMaterial.getFingerprint());
|
assertEquals(HARRY_FP, trustRootMaterial.getFingerprint());
|
||||||
|
@ -217,8 +229,8 @@ public class PGPCertificateDirectoryTest {
|
||||||
Certificate trustRootCert = directory.getTrustRootCertificate();
|
Certificate trustRootCert = directory.getTrustRootCertificate();
|
||||||
assertEquals(HARRY_FP, trustRootCert.getFingerprint());
|
assertEquals(HARRY_FP, trustRootCert.getFingerprint());
|
||||||
|
|
||||||
directory.tryInsert(new ByteArrayInputStream(RON_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
directory.tryInsert(new ByteArrayInputStream(RON_CERT.getBytes(UTF8)), merger);
|
||||||
directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), new TestKeyMaterialMerger());
|
directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||||
|
|
||||||
Set<String> expected = new HashSet<>(Arrays.asList(RON_FP, CEDRIC_FP));
|
Set<String> expected = new HashSet<>(Arrays.asList(RON_FP, CEDRIC_FP));
|
||||||
|
|
||||||
|
@ -230,4 +242,104 @@ public class PGPCertificateDirectoryTest {
|
||||||
|
|
||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideTestSubjects")
|
||||||
|
public void testGetTrustRootIfChanged(PGPCertificateDirectory directory)
|
||||||
|
throws BadDataException, IOException, InterruptedException {
|
||||||
|
KeyMaterial trustRootMaterial = directory.insertTrustRoot(
|
||||||
|
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||||
|
|
||||||
|
assertNotNull(trustRootMaterial.getTag());
|
||||||
|
Long tag = trustRootMaterial.getTag();
|
||||||
|
assertNull(directory.getTrustRootCertificateIfChanged(tag));
|
||||||
|
assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1));
|
||||||
|
|
||||||
|
Long oldTag = tag;
|
||||||
|
// "update" key
|
||||||
|
trustRootMaterial = directory.insertTrustRoot(
|
||||||
|
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||||
|
tag = trustRootMaterial.getTag();
|
||||||
|
|
||||||
|
assertNotEquals(oldTag, tag);
|
||||||
|
assertNotNull(directory.getTrustRootCertificateIfChanged(oldTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideTestSubjects")
|
||||||
|
public void testGetBySpecialNameIfChanged(PGPCertificateDirectory directory)
|
||||||
|
throws BadDataException, IOException, InterruptedException, BadNameException {
|
||||||
|
KeyMaterial specialName = directory.insertWithSpecialName(SpecialNames.TRUST_ROOT,
|
||||||
|
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||||
|
|
||||||
|
assertNotNull(specialName.getTag());
|
||||||
|
Long tag = specialName.getTag();
|
||||||
|
assertNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag));
|
||||||
|
assertNotNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, tag + 1));
|
||||||
|
|
||||||
|
Long oldTag = tag;
|
||||||
|
// "update" key
|
||||||
|
specialName = directory.insertWithSpecialName(SpecialNames.TRUST_ROOT,
|
||||||
|
new ByteArrayInputStream(HARRY_KEY.getBytes(UTF8)), merger);
|
||||||
|
tag = specialName.getTag();
|
||||||
|
|
||||||
|
assertNotEquals(oldTag, tag);
|
||||||
|
assertNotNull(directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, oldTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideTestSubjects")
|
||||||
|
public void testGetByFingerprintIfChanged(PGPCertificateDirectory directory)
|
||||||
|
throws BadDataException, IOException, InterruptedException, BadNameException {
|
||||||
|
Certificate certificate = directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||||
|
Long tag = certificate.getTag();
|
||||||
|
assertNotNull(tag);
|
||||||
|
|
||||||
|
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
|
||||||
|
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag + 1));
|
||||||
|
|
||||||
|
Long oldTag = tag;
|
||||||
|
// "update" cert
|
||||||
|
certificate = directory.insert(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), merger);
|
||||||
|
tag = certificate.getTag();
|
||||||
|
|
||||||
|
assertNotEquals(oldTag, tag);
|
||||||
|
assertNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), tag));
|
||||||
|
assertNotNull(directory.getByFingerprintIfChanged(certificate.getFingerprint(), oldTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(new ByteArrayInputStream(CEDRIC_CERT.getBytes(UTF8)), 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.cert_d;
|
package pgp.cert_d.dummy;
|
||||||
|
|
||||||
import pgp.certificate_store.certificate.KeyMaterial;
|
import pgp.certificate_store.certificate.KeyMaterial;
|
||||||
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
import pgp.certificate_store.certificate.KeyMaterialMerger;
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.cert_d;
|
package pgp.cert_d.dummy;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
|
@ -33,24 +33,22 @@ public class TestKeyMaterialReaderBackend implements KeyMaterialReaderBackend {
|
||||||
KeyFingerPrintCalculator fpCalc = new BcKeyFingerprintCalculator();
|
KeyFingerPrintCalculator fpCalc = new BcKeyFingerprintCalculator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyMaterial read(InputStream data) throws IOException, BadDataException {
|
public KeyMaterial read(InputStream data, Long tag) throws IOException, BadDataException {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
Streams.pipeAll(data, out);
|
Streams.pipeAll(data, out);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Key key = readKey(new ByteArrayInputStream(out.toByteArray()));
|
return readKey(new ByteArrayInputStream(out.toByteArray()), tag);
|
||||||
return key;
|
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
try {
|
try {
|
||||||
Certificate certificate = readCertificate(new ByteArrayInputStream(out.toByteArray()));
|
return readCertificate(new ByteArrayInputStream(out.toByteArray()), tag);
|
||||||
return certificate;
|
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
throw new BadDataException();
|
throw new BadDataException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Key readKey(InputStream inputStream) throws IOException, PGPException {
|
private Key readKey(InputStream inputStream, Long tag) throws IOException, PGPException {
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
Streams.pipeAll(inputStream, buffer);
|
Streams.pipeAll(inputStream, buffer);
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
|
@ -60,64 +58,21 @@ public class TestKeyMaterialReaderBackend implements KeyMaterialReaderBackend {
|
||||||
PGPSecretKeyRing secretKeys = new PGPSecretKeyRing(decoderStream, fpCalc);
|
PGPSecretKeyRing secretKeys = new PGPSecretKeyRing(decoderStream, fpCalc);
|
||||||
PGPPublicKeyRing cert = extractCert(secretKeys);
|
PGPPublicKeyRing cert = extractCert(secretKeys);
|
||||||
ByteArrayInputStream encoded = new ByteArrayInputStream(cert.getEncoded());
|
ByteArrayInputStream encoded = new ByteArrayInputStream(cert.getEncoded());
|
||||||
Certificate certificate = readCertificate(encoded);
|
Certificate certificate = readCertificate(encoded, tag);
|
||||||
|
|
||||||
return new Key() {
|
return new Key(buffer.toByteArray(), certificate, tag);
|
||||||
@Override
|
|
||||||
public Certificate getCertificate() {
|
|
||||||
return certificate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFingerprint() {
|
|
||||||
return certificate.getFingerprint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream() throws IOException {
|
|
||||||
return new ByteArrayInputStream(buffer.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Long> getSubkeyIds() throws IOException {
|
|
||||||
return certificate.getSubkeyIds();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Certificate readCertificate(InputStream inputStream) throws IOException {
|
private Certificate readCertificate(InputStream inputStream, Long tag) throws IOException {
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
Streams.pipeAll(inputStream, buffer);
|
Streams.pipeAll(inputStream, buffer);
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(buffer.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(buffer.toByteArray());
|
||||||
InputStream decoderStream = PGPUtil.getDecoderStream(in);
|
InputStream decoderStream = PGPUtil.getDecoderStream(in);
|
||||||
|
|
||||||
PGPPublicKeyRing cert = new PGPPublicKeyRing(decoderStream, fpCalc);
|
PGPPublicKeyRing cert = new PGPPublicKeyRing(decoderStream, fpCalc);
|
||||||
return new Certificate() {
|
String fingerprint = Hex.toHexString(cert.getPublicKey().getFingerprint()).toLowerCase();
|
||||||
@Override
|
List<Long> subKeyIds = getSubkeyIds(cert);
|
||||||
public String getFingerprint() {
|
return new Certificate(buffer.toByteArray(), fingerprint, subKeyIds, tag);
|
||||||
return Hex.toHexString(cert.getPublicKey().getFingerprint()).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream() throws IOException {
|
|
||||||
return new ByteArrayInputStream(buffer.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Long> getSubkeyIds() throws IOException {
|
|
||||||
return TestKeyMaterialReaderBackend.getSubkeyIds(cert);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPPublicKeyRing extractCert(PGPSecretKeyRing secretKeys) {
|
private PGPPublicKeyRing extractCert(PGPSecretKeyRing secretKeys) {
|
||||||
|
@ -126,8 +81,7 @@ public class TestKeyMaterialReaderBackend implements KeyMaterialReaderBackend {
|
||||||
while (publicKeyIterator.hasNext()) {
|
while (publicKeyIterator.hasNext()) {
|
||||||
publicKeyList.add(publicKeyIterator.next());
|
publicKeyList.add(publicKeyIterator.next());
|
||||||
}
|
}
|
||||||
PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing(publicKeyList);
|
return new PGPPublicKeyRing(publicKeyList);
|
||||||
return publicKeyRing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Long> getSubkeyIds(PGPKeyRing keyRing) {
|
private static List<Long> getSubkeyIds(PGPKeyRing keyRing) {
|
|
@ -32,6 +32,23 @@ public interface PGPCertificateStore {
|
||||||
Certificate getCertificate(String identifier)
|
Certificate getCertificate(String identifier)
|
||||||
throws IOException, BadNameException, BadDataException;
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the certificate that matches the given identifier, but only if it has been changed.
|
||||||
|
* Whether it has been changed is determined by calculating the tag in the directory
|
||||||
|
* (e.g. by looking at the inode and last modification date) and comparing the result with the tag provided by
|
||||||
|
* the caller.
|
||||||
|
*
|
||||||
|
* @param identifier certificate identifier
|
||||||
|
* @param tag tag by the caller
|
||||||
|
* @return certificate if it has been changed, null otherwise
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an IO-error
|
||||||
|
* @throws BadNameException if the identifier is invalid
|
||||||
|
* @throws BadDataException if the certificate file contains invalid data
|
||||||
|
*/
|
||||||
|
Certificate getCertificateIfChanged(String identifier, Long tag)
|
||||||
|
throws IOException, BadNameException, BadDataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an {@link Iterator} over all certificates in the store that contain a subkey with the given
|
* Return an {@link Iterator} over all certificates in the store that contain a subkey with the given
|
||||||
* subkey id.
|
* subkey id.
|
||||||
|
@ -42,7 +59,7 @@ public interface PGPCertificateStore {
|
||||||
* @throws BadDataException if any of the certificate files contains invalid data
|
* @throws BadDataException if any of the certificate files contains invalid data
|
||||||
*/
|
*/
|
||||||
Iterator<Certificate> getCertificatesBySubkeyId(long subkeyId)
|
Iterator<Certificate> getCertificatesBySubkeyId(long subkeyId)
|
||||||
throws IOException, BadDataException;
|
throws IOException, BadDataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a certificate into the store.
|
* Insert a certificate into the store.
|
||||||
|
|
|
@ -4,13 +4,67 @@
|
||||||
|
|
||||||
package pgp.certificate_store.certificate;
|
package pgp.certificate_store.certificate;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenPGP certificate (public key).
|
* OpenPGP certificate (public key).
|
||||||
*/
|
*/
|
||||||
public abstract class Certificate implements KeyMaterial {
|
public class Certificate implements KeyMaterial {
|
||||||
|
|
||||||
|
private final byte[] bytes;
|
||||||
|
private final String fingerprint;
|
||||||
|
private final List<Long> subkeyIds;
|
||||||
|
private final Long tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certificate constructor.
|
||||||
|
*
|
||||||
|
* @param bytes encoding of the certificate
|
||||||
|
* @param fingerprint fingerprint (lowercase hex characters)
|
||||||
|
* @param subkeyIds list of subkey ids
|
||||||
|
* @param tag tag
|
||||||
|
*/
|
||||||
|
public Certificate(byte[] bytes, String fingerprint, List<Long> subkeyIds, Long tag) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
this.subkeyIds = subkeyIds;
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor to assign a new tag to the {@link Certificate}.
|
||||||
|
*
|
||||||
|
* @param cert certificate
|
||||||
|
* @param tag tag
|
||||||
|
*/
|
||||||
|
public Certificate(Certificate cert, Long tag) {
|
||||||
|
this(cert.bytes, cert.fingerprint, cert.subkeyIds, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFingerprint() {
|
||||||
|
return fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate asCertificate() {
|
public Certificate asCertificate() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return new ByteArrayInputStream(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getSubkeyIds() {
|
||||||
|
return subkeyIds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,74 @@
|
||||||
|
|
||||||
package pgp.certificate_store.certificate;
|
package pgp.certificate_store.certificate;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenPGP key (secret key).
|
* OpenPGP key (secret key).
|
||||||
*/
|
*/
|
||||||
public abstract class Key implements KeyMaterial {
|
public class Key implements KeyMaterial {
|
||||||
|
|
||||||
|
private final byte[] bytes;
|
||||||
|
private final Certificate certificate;
|
||||||
|
private final Long tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key constructor.
|
||||||
|
*
|
||||||
|
* @param bytes encoding of the key
|
||||||
|
* @param certificate associated certificate
|
||||||
|
* @param tag tag
|
||||||
|
*/
|
||||||
|
public Key(byte[] bytes, Certificate certificate, Long tag) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
this.certificate = certificate;
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor to change the tag of both the {@link Key} and its {@link Certificate}.
|
||||||
|
*
|
||||||
|
* @param key key
|
||||||
|
* @param tag tag
|
||||||
|
*/
|
||||||
|
public Key(Key key, Long tag) {
|
||||||
|
this(key.bytes, new Certificate(key.certificate, tag), tag);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the certificate part of this OpenPGP key.
|
* Return the certificate part of this OpenPGP key.
|
||||||
*
|
*
|
||||||
* @return OpenPGP certificate
|
* @return OpenPGP certificate
|
||||||
*/
|
*/
|
||||||
public abstract Certificate getCertificate();
|
public Certificate getCertificate() {
|
||||||
|
return new Certificate(certificate, getTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFingerprint() {
|
||||||
|
return certificate.getFingerprint();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Certificate asCertificate() {
|
public Certificate asCertificate() {
|
||||||
return getCertificate();
|
return getCertificate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return new ByteArrayInputStream(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getSubkeyIds() {
|
||||||
|
return certificate.getSubkeyIds();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
package pgp.certificate_store.certificate;
|
package pgp.certificate_store.certificate;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -19,23 +18,34 @@ public interface KeyMaterial {
|
||||||
*/
|
*/
|
||||||
String getFingerprint();
|
String getFingerprint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link Certificate} belonging to this key material.
|
||||||
|
* If this is already a {@link Certificate}, return this.
|
||||||
|
* If this is a {@link Key}, extract the {@link Certificate} and return it.
|
||||||
|
*
|
||||||
|
* @return certificate
|
||||||
|
*/
|
||||||
Certificate asCertificate();
|
Certificate asCertificate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an {@link InputStream} of the binary representation of the secret key.
|
* Return an {@link InputStream} of the binary representation of the secret key.
|
||||||
*
|
*
|
||||||
* @return input stream
|
* @return input stream
|
||||||
* @throws IOException in case of an IO error
|
|
||||||
*/
|
*/
|
||||||
InputStream getInputStream() throws IOException;
|
InputStream getInputStream();
|
||||||
|
|
||||||
String getTag() throws IOException;
|
/**
|
||||||
|
* Return the tag belonging to this key material.
|
||||||
|
* The tag can be used to keep an application cache in sync with what is in the directory.
|
||||||
|
*
|
||||||
|
* @return tag
|
||||||
|
*/
|
||||||
|
Long getTag();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@link Set} containing key-ids of subkeys.
|
* Return a {@link Set} containing key-ids of subkeys.
|
||||||
*
|
*
|
||||||
* @return subkeys
|
* @return subkeys
|
||||||
* @throws IOException in case of an IO error
|
|
||||||
*/
|
*/
|
||||||
List<Long> getSubkeyIds() throws IOException;
|
List<Long> getSubkeyIds();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ public interface KeyMaterialReaderBackend {
|
||||||
* @throws IOException in case of an IO error
|
* @throws IOException in case of an IO error
|
||||||
* @throws BadDataException in case that the data stream does not contain a valid OpenPGP key/certificate
|
* @throws BadDataException in case that the data stream does not contain a valid OpenPGP key/certificate
|
||||||
*/
|
*/
|
||||||
KeyMaterial read(InputStream data) throws IOException, BadDataException;
|
KeyMaterial read(InputStream data, Long tag) throws IOException, BadDataException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ allprojects {
|
||||||
ext {
|
ext {
|
||||||
shortVersion = '0.1.2'
|
shortVersion = '0.1.2'
|
||||||
isSnapshot = true
|
isSnapshot = true
|
||||||
minAndroidSdk = 10
|
minAndroidSdk = 26
|
||||||
javaSourceCompatibility = 1.8
|
javaSourceCompatibility = 1.8
|
||||||
bouncycastleVersion = '1.71'
|
bouncycastleVersion = '1.71'
|
||||||
slf4jVersion = '1.7.36'
|
slf4jVersion = '1.7.36'
|
||||||
|
|
Loading…
Reference in a new issue