mirror of
https://codeberg.org/PGPainless/wkd-java.git
synced 2025-09-09 03:09:39 +02:00
Modularize WKD discovery
This commit is contained in:
parent
30e8a55ef6
commit
d1d953e802
24 changed files with 511 additions and 111 deletions
|
@ -20,7 +20,7 @@ dependencies {
|
|||
testImplementation 'com.ginsberg:junit5-system-exit:1.1.2'
|
||||
testImplementation 'org.mockito:mockito-core:4.3.1'
|
||||
|
||||
implementation("org.pgpainless:pgpainless-core:$pgpainlessVersion")
|
||||
implementation("org.pgpainless:pgpainless-cert-d:0.1.0")
|
||||
implementation project(':wkd-java')
|
||||
implementation "info.picocli:picocli:4.6.3"
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.wkd.cli;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.certificate_store.CertificateFactory;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import pgp.certificate_store.Certificate;
|
||||
import pgp.wkd.CertificateAndUserIds;
|
||||
import pgp.wkd.CertificateReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CertificateReaderImpl implements CertificateReader {
|
||||
@Override
|
||||
public List<CertificateAndUserIds> read(InputStream inputStream) throws IOException {
|
||||
List<CertificateAndUserIds> certificatesAndUserIds = new ArrayList<>();
|
||||
try {
|
||||
PGPPublicKeyRingCollection certificates = PGPainless.readKeyRing().publicKeyRingCollection(inputStream);
|
||||
for (PGPPublicKeyRing certificate : certificates) {
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(certificate);
|
||||
Certificate parsedCert = CertificateFactory.certificateFromPublicKeyRing(certificate);
|
||||
List<String> userIds = info.getValidAndExpiredUserIds();
|
||||
certificatesAndUserIds.add(new CertificateAndUserIds(parsedCert, userIds));
|
||||
}
|
||||
return certificatesAndUserIds;
|
||||
} catch (PGPException e) {
|
||||
throw new IOException("Cannot parse certificates.", e);
|
||||
}
|
||||
}
|
||||
}
|
21
wkd-java-cli/src/main/java/pgp/wkd/cli/DiscoverImpl.java
Normal file
21
wkd-java-cli/src/main/java/pgp/wkd/cli/DiscoverImpl.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package pgp.wkd.cli;
|
||||
|
||||
import pgp.wkd.AbstractDiscover;
|
||||
import pgp.wkd.CertificateReader;
|
||||
import pgp.wkd.HttpUrlConnectionWKDFetcher;
|
||||
import pgp.wkd.WKDFetcher;
|
||||
|
||||
public class DiscoverImpl extends AbstractDiscover {
|
||||
|
||||
public DiscoverImpl() {
|
||||
super(new CertificateReaderImpl(), new HttpUrlConnectionWKDFetcher());
|
||||
}
|
||||
|
||||
public DiscoverImpl(WKDFetcher fetcher) {
|
||||
super(new CertificateReaderImpl(), fetcher);
|
||||
}
|
||||
|
||||
public DiscoverImpl(CertificateReader certificateReader, WKDFetcher fetcher) {
|
||||
super(certificateReader, fetcher);
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.wkd.cli;
|
||||
|
||||
/**
|
||||
* Exception that gets thrown when an OpenPGP certificate is not carrying a User-ID binding for the email address
|
||||
* that was used to look the certificate up via WKD.
|
||||
*/
|
||||
public class MissingUserIdException extends RuntimeException {
|
||||
|
||||
public static final int ERROR_CODE = 7;
|
||||
|
||||
public MissingUserIdException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package pgp.wkd.cli;
|
||||
|
||||
import pgp.wkd.MissingUserIdException;
|
||||
import pgp.wkd.cli.command.Fetch;
|
||||
import picocli.CommandLine;
|
||||
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
package pgp.wkd.cli.command;
|
||||
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import pgp.wkd.AbstractWKDFetcher;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import pgp.certificate_store.Certificate;
|
||||
import pgp.wkd.Discover;
|
||||
import pgp.wkd.HttpUrlConnectionWKDFetcher;
|
||||
import pgp.wkd.MalformedUserIdException;
|
||||
import pgp.wkd.WKDAddress;
|
||||
import pgp.wkd.WKDAddressHelper;
|
||||
import pgp.wkd.WKDDiscoveryResult;
|
||||
import pgp.wkd.WKDFetcher;
|
||||
import pgp.wkd.cli.CertNotFetchableException;
|
||||
import pgp.wkd.cli.MissingUserIdException;
|
||||
import pgp.wkd.cli.DiscoverImpl;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "fetch",
|
||||
|
@ -42,47 +42,44 @@ public class Fetch implements Runnable {
|
|||
boolean armor = false;
|
||||
|
||||
// TODO: Better way to inject fetcher implementation
|
||||
public static AbstractWKDFetcher fetcher = new HttpUrlConnectionWKDFetcher();
|
||||
public static WKDFetcher fetcher = new HttpUrlConnectionWKDFetcher();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String email;
|
||||
try {
|
||||
email = WKDAddressHelper.emailFromUserId(userId);
|
||||
} catch (IllegalArgumentException e) {
|
||||
email = userId;
|
||||
Discover discover = new DiscoverImpl(fetcher);
|
||||
|
||||
WKDAddress address = addressFromUserId(userId);
|
||||
WKDDiscoveryResult result = discover.discover(address);
|
||||
|
||||
if (!result.isSuccessful()) {
|
||||
throw new CertNotFetchableException("Cannot fetch cert.");
|
||||
}
|
||||
|
||||
WKDAddress address = WKDAddress.fromEmail(email);
|
||||
try (InputStream inputStream = fetcher.fetch(address)) {
|
||||
PGPPublicKeyRing cert = PGPainless.readKeyRing().publicKeyRing(inputStream);
|
||||
if (cert == null) {
|
||||
throw new CertNotFetchableException("Fetched data does not contain an OpenPGP certificate.");
|
||||
}
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(cert);
|
||||
|
||||
List<String> userIds = info.getValidAndExpiredUserIds();
|
||||
boolean containsEmail = false;
|
||||
for (String certUserId : userIds) {
|
||||
if (certUserId.contains("<" + email + ">") || certUserId.equals(email)) {
|
||||
containsEmail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!containsEmail) {
|
||||
throw new MissingUserIdException("Fetched certificate does not contain email address " + email);
|
||||
}
|
||||
|
||||
try {
|
||||
if (armor) {
|
||||
OutputStream out = new ArmoredOutputStream(System.out);
|
||||
cert.encode(out);
|
||||
for (Certificate certificate : result.getCertificates()) {
|
||||
Streams.pipeAll(certificate.getInputStream(), out);
|
||||
}
|
||||
out.close();
|
||||
} else {
|
||||
cert.encode(System.out);
|
||||
for (Certificate certificate : result.getCertificates()) {
|
||||
Streams.pipeAll(certificate.getInputStream(), System.out);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new CertNotFetchableException("Certificate cannot be fetched.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private WKDAddress addressFromUserId(String userId) {
|
||||
String email;
|
||||
try {
|
||||
email = WKDAddressHelper.emailFromUserId(userId);
|
||||
} catch (MalformedUserIdException e) {
|
||||
email = userId;
|
||||
}
|
||||
|
||||
return WKDAddress.fromEmail(email);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
package pgp.wkd.cli.test_suite;
|
||||
|
||||
import pgp.wkd.AbstractWKDFetcher;
|
||||
import pgp.wkd.DiscoveryMethod;
|
||||
import pgp.wkd.WKDAddress;
|
||||
import pgp.wkd.WKDFetcher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -13,7 +15,7 @@ import java.io.InputStream;
|
|||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class DirectoryBasedWkdFetcher extends AbstractWKDFetcher {
|
||||
public class DirectoryBasedWkdFetcher implements WKDFetcher {
|
||||
|
||||
// The directory containing the .well-known subdirectory
|
||||
private final Path rootPath;
|
||||
|
@ -23,7 +25,8 @@ public class DirectoryBasedWkdFetcher extends AbstractWKDFetcher {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected InputStream fetchUri(URI uri) throws IOException {
|
||||
public InputStream fetch(WKDAddress address, DiscoveryMethod method) throws IOException {
|
||||
URI uri = address.getUri(method);
|
||||
String path = uri.getPath();
|
||||
File file = rootPath.resolve(path.substring(1)).toFile(); // get rid of leading slash at start of path
|
||||
FileInputStream fileIn = new FileInputStream(file);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue