Compare commits
35 Commits
Author | SHA1 | Date |
---|---|---|
Paul Schaub | 57434d80cf | |
Paul Schaub | 32a2909c53 | |
Paul Schaub | 23cc79aaa8 | |
Paul Schaub | 3f68ae5725 | |
Paul Schaub | 09f45e86db | |
Paul Schaub | ec59e717fc | |
Paul Schaub | 1d65b4b3c2 | |
Paul Schaub | e37921b4d4 | |
Paul Schaub | f349c701fa | |
Paul Schaub | 867eebe684 | |
Paul Schaub | 0a21cec1d3 | |
Paul Schaub | 327d216648 | |
Paul Schaub | 6074feff9b | |
Paul Schaub | 03354a18a6 | |
Paul Schaub | 8aa68ccfb3 | |
Paul Schaub | 1def42b54b | |
Paul Schaub | b1d3b42d39 | |
Paul Schaub | 80ff3cf47a | |
Paul Schaub | d82b22131f | |
Paul Schaub | 29cfade707 | |
Paul Schaub | ac101a1413 | |
Paul Schaub | fd191c1bb4 | |
Paul Schaub | 028daa6a73 | |
Paul Schaub | a35378e870 | |
Paul Schaub | f16abd3a5e | |
Paul Schaub | 702ea0016e | |
Paul Schaub | 635b88b093 | |
Paul Schaub | 661f7865f4 | |
Paul Schaub | 0dc7ba1ecb | |
Paul Schaub | a36e01d7d2 | |
Paul Schaub | 0ebfadac2a | |
Paul Schaub | c473e1712c | |
Paul Schaub | 181af197b5 | |
Paul Schaub | 352f6d3c7f | |
Paul Schaub | d5f3ca9ea1 |
|
@ -13,3 +13,8 @@ Source: https://pgpainless.org
|
||||||
Files: gradle*
|
Files: gradle*
|
||||||
Copyright: 2015 the original author or authors.
|
Copyright: 2015 the original author or authors.
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
|
|
||||||
|
# Woodpecker build files
|
||||||
|
Files: .woodpecker/*
|
||||||
|
Copyright: 2022 the original author or authors.
|
||||||
|
License: Apache-2.0
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
pipeline:
|
||||||
|
run:
|
||||||
|
image: gradle:7.5-jdk8
|
||||||
|
commands:
|
||||||
|
- git checkout $CI_COMMIT_BRANCH
|
||||||
|
# Code works
|
||||||
|
- gradle test
|
||||||
|
# Code is clean
|
||||||
|
- gradle check javadocAll
|
||||||
|
# Code has coverage
|
||||||
|
- gradle jacocoRootReport coveralls
|
||||||
|
secrets: [COVERALLS_REPO_TOKEN]
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Code is licensed properly
|
||||||
|
# See https://reuse.software/
|
||||||
|
pipeline:
|
||||||
|
reuse:
|
||||||
|
image: fsfe/reuse:latest
|
||||||
|
commands:
|
||||||
|
- reuse lint
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
- Bump `bc-util` to `1.75`
|
||||||
|
- Bump `jackson-databind` to `2.15.2`
|
||||||
|
- Add support for resource bundles for i18n
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
- Bump `slf4j` to `1.7.36`
|
||||||
|
- Bump `logback` to `1.2.11`
|
||||||
|
- Bump `lombok` to `1.18.24`
|
||||||
|
- CLI: Set `keys.openpgp.org` as default key server
|
||||||
|
- Add name and description to parent command
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
- Add `vks-java-cli`: CLI frontend for `vks-java`
|
||||||
|
- Bump Bouncy Castle dependencies to `1.71`
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
- Initial release
|
||||||
|
- `vks-java`: Client side API to communicate with Verifying Key Servers
|
11
README.md
11
README.md
|
@ -6,6 +6,17 @@ SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
# Verifying Key Server - Client API for Java
|
# Verifying Key Server - Client API for Java
|
||||||
|
|
||||||
|
[![status-badge](https://ci.codeberg.org/api/badges/PGPainless/vks-java/status.svg)](https://ci.codeberg.org/PGPainless/vks-java)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/pgpainless/vks-java/badge.svg?branch=main)](https://coveralls.io/github/pgpainless/vks-java?branch=main)
|
||||||
|
[![REUSE status](https://api.reuse.software/badge/github.com/pgpainless/vks-java)](https://api.reuse.software/info/github.com/pgpainless/vks-java)
|
||||||
|
|
||||||
Client-side API for fetching keys from - and publishing keys to - Verifying OpenPGP Key Servers (VKS).
|
Client-side API for fetching keys from - and publishing keys to - Verifying OpenPGP Key Servers (VKS).
|
||||||
|
|
||||||
An example implementation of a Verifying Key Server is [Hagrid](https://gitlab.com/hagrid-keyserver/hagrid), which is running https://keys.openpgp.org.
|
An example implementation of a Verifying Key Server is [Hagrid](https://gitlab.com/hagrid-keyserver/hagrid), which is running https://keys.openpgp.org.
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
This repository contains the following modules:
|
||||||
|
|
||||||
|
* [vks-java](/vks-java): Client-side Java API for communicating with VKS servers
|
||||||
|
* [vks-java-cli](/vks-java-cli): CLI frontend for `vks-java`
|
||||||
|
|
|
@ -70,9 +70,6 @@ allprojects {
|
||||||
}
|
}
|
||||||
|
|
||||||
project.ext {
|
project.ext {
|
||||||
junitVersion = '5.8.2'
|
|
||||||
slf4jVersion = '1.7.32'
|
|
||||||
logbackVersion = '1.2.10'
|
|
||||||
rootConfigDir = new File(rootDir, 'config')
|
rootConfigDir = new File(rootDir, 'config')
|
||||||
gitCommit = getGitCommit()
|
gitCommit = getGitCommit()
|
||||||
isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI'))
|
isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI'))
|
||||||
|
|
|
@ -4,4 +4,5 @@
|
||||||
|
|
||||||
rootProject.name = 'VKS-Java'
|
rootProject.name = 'VKS-Java'
|
||||||
|
|
||||||
include 'vks-java'
|
include 'vks-java',
|
||||||
|
'vks-java-cli'
|
|
@ -4,9 +4,17 @@
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
ext {
|
ext {
|
||||||
shortVersion = '0.1.0'
|
shortVersion = '0.1.4'
|
||||||
isSnapshot = false
|
isSnapshot = true
|
||||||
minAndroidSdk = 10
|
minAndroidSdk = 10
|
||||||
javaSourceCompatibility = 1.8
|
javaSourceCompatibility = 1.8
|
||||||
|
bouncycastleVersion = '1.75'
|
||||||
|
junitVersion = '5.8.2'
|
||||||
|
jsrVersion = '3.0.2'
|
||||||
|
slf4jVersion = '1.7.36'
|
||||||
|
logbackVersion = '1.2.11'
|
||||||
|
lombokVersion = '1.18.24'
|
||||||
|
picocliVersion = '4.6.3'
|
||||||
|
jacksonDataBindVersion = '2.15.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# VKS-Java-CLI
|
||||||
|
|
||||||
|
[![javadoc](https://javadoc.io/badge2/org.pgpainless/vks-java-cli/javadoc.svg)](https://javadoc.io/doc/org.pgpainless/vks-java-cli)
|
||||||
|
[![Maven Central](https://badgen.net/maven/v/maven-central/org.pgpainless/vks-java-cli)](https://search.maven.org/artifact/org.pgpainless/vks-java-cli)
|
||||||
|
|
||||||
|
Command Line Frontend for VKS-Java
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To build the CLI app, use `gradle build`. An archive containing an executable can then be found in `vks-java-cli/build/distributions/`.
|
||||||
|
Extract it and navigate to the `bin` subdirectory, where you can find `vks-java-cli`/`vks-java-cli.bat` executables.
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Usage: vkscli [COMMAND]
|
||||||
|
Commands:
|
||||||
|
help Displays help information about the specified command
|
||||||
|
get Retrieve an OpenPGP certificate from the key server
|
||||||
|
upload Upload an OpenPGP certificate to the key server
|
||||||
|
request-verification Request verification for unverified user-ids
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the CLI application uses `https://keys.openpgp.org` as key server.
|
||||||
|
To use another VKS, use the option `--key-server https://your.key.server` in any command.
|
||||||
|
|
||||||
|
To retrieve a key from a Verifying Key Server, use the `get` subcommand:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./vks-java-cli get -e vanitasvitae@fsfe.org > foo.asc
|
||||||
|
$ ./vks-java-cli get -f 7F9116FEA90A5983936C7CFAA027DB2F3E1E118A > foo.asc
|
||||||
|
$ ./vks-java-cli get -i -2535611045697927659 > foo.asc
|
||||||
|
```
|
||||||
|
|
||||||
|
To upload a key, use the `upload` subcommand:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./vks-java-cli upload -r < foo.asc
|
||||||
|
```
|
||||||
|
|
||||||
|
The option `-r` automatically requests verification mails for unpublished mail addresses.
|
||||||
|
|
||||||
|
To manually request verification mails, use the `request-verification` subcommand, passing it the token acquired by the `upload` command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./vks-java-cli request-verification -t <upload-token> -a foo@bar.baz -a other@email.address
|
||||||
|
```
|
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'application'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'org.pgpainless'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// JUnit
|
||||||
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||||
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
|
|
||||||
|
// VKS-Java
|
||||||
|
implementation project(":vks-java")
|
||||||
|
|
||||||
|
// CLI
|
||||||
|
implementation "info.picocli:picocli:$picocliVersion"
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass = 'pgp.vks.client.cli.VKSCLI'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.vks.client.cli;
|
||||||
|
|
||||||
|
import pgp.vks.client.Get;
|
||||||
|
import pgp.vks.client.VKS;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "get",
|
||||||
|
resourceBundle = "msg_get")
|
||||||
|
public class GetCmd implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
VKSCLI.KeyServerMixin keyServerMixin;
|
||||||
|
|
||||||
|
@CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
|
||||||
|
Exclusive by;
|
||||||
|
|
||||||
|
static class Exclusive {
|
||||||
|
@CommandLine.Option(names = {"-f", "--by-fingerprint"})
|
||||||
|
String fingerprint;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-i", "--by-keyid"})
|
||||||
|
Long keyId;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-e", "--by-email"})
|
||||||
|
String email;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ResourceBundle msg;
|
||||||
|
|
||||||
|
public GetCmd() {
|
||||||
|
msg = ResourceBundle.getBundle("msg_get", Locale.getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
VKS vks;
|
||||||
|
try {
|
||||||
|
vks = keyServerMixin.parent.getApi();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Get get = vks.get();
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
if (by.fingerprint != null) {
|
||||||
|
inputStream = get.byFingerprint(by.fingerprint);
|
||||||
|
} else if (by.keyId != null) {
|
||||||
|
inputStream = get.byKeyId(by.keyId);
|
||||||
|
} else if (by.email != null) {
|
||||||
|
inputStream = get.byEmail(by.email);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(msg.getString("error.missing_by_option"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int read;
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
while ((read = inputStream.read(buf)) != -1) {
|
||||||
|
System.out.write(buf, 0, read);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.vks.client.cli;
|
||||||
|
|
||||||
|
import pgp.vks.client.RequestVerify;
|
||||||
|
import pgp.vks.client.VKS;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "request-verification",
|
||||||
|
resourceBundle = "msg_request_verification")
|
||||||
|
public class RequestVerificationCmd implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
VKSCLI.KeyServerMixin keyServerMixin;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-t", "--token"},
|
||||||
|
required = true, arity = "1", paramLabel = "TOKEN")
|
||||||
|
String token;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-l", "--locale"})
|
||||||
|
List<String> locale = Arrays.asList("en_US", "en_GB");
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-e", "--email"}, required = true, arity = "1..*")
|
||||||
|
String[] addresses = new String[0];
|
||||||
|
|
||||||
|
private final ResourceBundle msg;
|
||||||
|
|
||||||
|
public RequestVerificationCmd() {
|
||||||
|
msg = ResourceBundle.getBundle("msg_request_verification", Locale.getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
VKS vks;
|
||||||
|
try {
|
||||||
|
vks = keyServerMixin.parent.getApi();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestVerify requestVerify = vks.requestVerification();
|
||||||
|
try {
|
||||||
|
RequestVerify.Response response = requestVerify
|
||||||
|
.forEmailAddresses(addresses)
|
||||||
|
.execute(token, locale);
|
||||||
|
System.out.printf(msg.getString("output.mails_sent"), response.getKeyFingerprint());
|
||||||
|
System.out.printf(msg.getString("output.token"), response.getToken());
|
||||||
|
System.out.println(msg.getString("output.status"));
|
||||||
|
for (String address : response.getStatus().keySet()) {
|
||||||
|
System.out.println("\t" + address + "\t" + response.getStatus().get(address));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.vks.client.cli;
|
||||||
|
|
||||||
|
import pgp.vks.client.RequestVerify;
|
||||||
|
import pgp.vks.client.Status;
|
||||||
|
import pgp.vks.client.Upload;
|
||||||
|
import pgp.vks.client.VKS;
|
||||||
|
import pgp.vks.client.exception.CertCannotBePublishedException;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "upload",
|
||||||
|
resourceBundle = "msg_upload")
|
||||||
|
public class UploadCmd implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
VKSCLI.KeyServerMixin keyServerMixin;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-r", "--request-verification"})
|
||||||
|
boolean requestVerification;
|
||||||
|
|
||||||
|
private final ResourceBundle msg;
|
||||||
|
|
||||||
|
public UploadCmd() {
|
||||||
|
msg = ResourceBundle.getBundle("msg_upload", Locale.getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
VKS vks;
|
||||||
|
try {
|
||||||
|
vks = keyServerMixin.parent.getApi();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Upload upload = vks.upload();
|
||||||
|
try {
|
||||||
|
Upload.Response response = upload.cert(System.in);
|
||||||
|
|
||||||
|
// Unpublished mail addresses
|
||||||
|
List<String> unpublished = new ArrayList<>();
|
||||||
|
int maxMailLen = 0;
|
||||||
|
for (String address : response.getStatus().keySet()) {
|
||||||
|
Status status = response.getStatus().get(address);
|
||||||
|
if (address.length() > maxMailLen) {
|
||||||
|
maxMailLen = address.length();
|
||||||
|
}
|
||||||
|
if (status != Status.published && status != Status.revoked) {
|
||||||
|
unpublished.add(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String msgUpload = String.format(msg.getString("output.uploaded_key"),
|
||||||
|
response.getKeyFingerprint(), response.getToken());
|
||||||
|
System.out.println(msgUpload);
|
||||||
|
|
||||||
|
String msgStatus = msg.getString("output.status");
|
||||||
|
if (!requestVerification || unpublished.isEmpty()) {
|
||||||
|
System.out.println(msgStatus);
|
||||||
|
for (String address : response.getStatus().keySet()) {
|
||||||
|
Status status = response.getStatus().get(address);
|
||||||
|
System.out.format("%-" + maxMailLen + "s %s\n", address, status);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestVerify.Response verifyResponse = vks.requestVerification().forEmailAddresses(unpublished.toArray(new String[0]))
|
||||||
|
.execute(response.getToken());
|
||||||
|
System.out.println(msgStatus);
|
||||||
|
for (String address : verifyResponse.getStatus().keySet()) {
|
||||||
|
Status status = response.getStatus().get(address);
|
||||||
|
System.out.format("%-" + maxMailLen + "s %s\n", address, status);
|
||||||
|
}
|
||||||
|
} catch (CertCannotBePublishedException e) {
|
||||||
|
throw new AssertionError(e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.vks.client.cli;
|
||||||
|
|
||||||
|
import pgp.vks.client.VKS;
|
||||||
|
import pgp.vks.client.VKSImpl;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "vks",
|
||||||
|
resourceBundle = "msg_vks",
|
||||||
|
subcommands = {
|
||||||
|
CommandLine.HelpCommand.class,
|
||||||
|
GetCmd.class,
|
||||||
|
UploadCmd.class,
|
||||||
|
RequestVerificationCmd.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class VKSCLI {
|
||||||
|
|
||||||
|
String keyServer = "https://keys.openpgp.org";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
int exitCode = execute(args);
|
||||||
|
if (exitCode != 0) {
|
||||||
|
System.exit(exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int execute(String[] args) {
|
||||||
|
CommandLine cmd = new CommandLine(VKSCLI.class);
|
||||||
|
cmd.setExitCodeExceptionMapper(new CommandLine.IExitCodeExceptionMapper() {
|
||||||
|
@Override
|
||||||
|
public int getExitCode(Throwable exception) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cmd.getSubcommands().get("help").setResourceBundle(ResourceBundle.getBundle("msg_help", Locale.getDefault()));
|
||||||
|
return cmd.setCommandName("vkscli")
|
||||||
|
.execute(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VKS getApi() throws MalformedURLException {
|
||||||
|
return new VKSImpl(keyServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KeyServerMixin {
|
||||||
|
|
||||||
|
@CommandLine.ParentCommand
|
||||||
|
VKSCLI parent;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = "--key-server",
|
||||||
|
paramLabel = "KEYSERVER")
|
||||||
|
public void setKeyServer(String keyServer) {
|
||||||
|
parent.keyServer = keyServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command Line Interface for VKS-Java.
|
||||||
|
*/
|
||||||
|
package pgp.vks.client.cli;
|
|
@ -0,0 +1,9 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Retrieve an OpenPGP certificate from the key server
|
||||||
|
by-fingerprint=Retrieve a key by its fingerprint (NOT prefixed with '0x')
|
||||||
|
by-keyid=Retrieve a key by its decimal key ID or that of one of its subkeys
|
||||||
|
by-email=Retrieve a key by email address
|
||||||
|
error.missing_by_option=Missing --by-* option.
|
||||||
|
key-server=Address of the Verifying Key Server.%nDefaults to 'https://keys.openpgp.org'
|
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Empfange ein OpenPGP Zertifikat vom Schlüsselserver
|
||||||
|
by-fingerprint=Finde das Zertifikat anhand seines Fingerabdrucks (OHNE Präfix '0x')
|
||||||
|
by-keyid=Finde das Zertifikat anhand seiner Schlüssel-ID oder der eines seiner Unterschlüssel
|
||||||
|
by-email=Finde das Zertifikat anhand einer E-Mail-Adresse
|
||||||
|
error.missing_by_option=Fehlende --by-* Option.
|
||||||
|
usage.synopsisHeading=Nutzung:\u0020
|
||||||
|
usage.optionListHeading=Optionen:%n
|
||||||
|
|
||||||
|
key-server=Adresse des verifizierenden Schlüsselservers.%nStandardmäßig: 'https://keys.openpgp.org'
|
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Displays help information about the specified command
|
||||||
|
usage.synopsisHeading=Usage:\u0020
|
|
@ -0,0 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Zeige Hilfetext für den angegebenen Befehl
|
||||||
|
usage.synopsisHeading=Nutzung:\u0020
|
||||||
|
usage.optionListHeading=Optionen:%n
|
|
@ -0,0 +1,11 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Request verification for unverified user-ids
|
||||||
|
token=Access token. Can be retrieved by uploading the certificate.
|
||||||
|
locale=Locale (language) for the verification mail
|
||||||
|
email=Email addresses to request a verification mail for
|
||||||
|
output.mails_sent=Verification E-Mails for certificate %s have been sent.%n
|
||||||
|
output.token=Token: %s%n
|
||||||
|
output.status=Status:
|
||||||
|
key-server=Address of the Verifying Key Server.%nDefaults to 'https://keys.openpgp.org'
|
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Fordere Verifikation von unveröffentlichten Nutzeridentitäten an
|
||||||
|
token=Zugangstoken. Kann durch das Hochladen des Zertifikates erhalten werden.
|
||||||
|
locale=Gebietsschema (Sprache) für die E-Mail-Verifikation
|
||||||
|
email=E-Mail-Adresse für die eine Verifikation angefragt werden soll
|
||||||
|
output.mails_sent=E-Mail-Verifikationen für Zertifikat %s wurden versendet.%n%n
|
||||||
|
output.token=Zugangstoken: %s%n
|
||||||
|
output.status=Status:
|
||||||
|
usage.synopsisHeading=Nutzung:\u0020
|
||||||
|
usage.optionListHeading=Optionen:%n
|
||||||
|
|
||||||
|
key-server=Adresse des verifizierenden Schlüsselservers.%nStandardmäßig: 'https://keys.openpgp.org'
|
|
@ -0,0 +1,8 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Upload an OpenPGP certificate to the key server
|
||||||
|
request-verification=Request verification mails for unpublished email addresses
|
||||||
|
output.uploaded_key=Uploaded key: %s%nToken: %s
|
||||||
|
output.status=Status:
|
||||||
|
key-server=Address of the Verifying Key Server.%nDefaults to 'https://keys.openpgp.org'
|
|
@ -0,0 +1,11 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Lade ein OpenPGP Zertifikat auf den Schlüsselserver hoch
|
||||||
|
request-verification=Fordere E-Mailverifikation für unveröffentlichte E-Mail-Adressen an
|
||||||
|
output.uploaded_key=Hochgeladenes Zertifikat: %s%nToken: %s
|
||||||
|
output.status=Status:
|
||||||
|
usage.synopsisHeading=Nutzung:\u0020
|
||||||
|
usage.optionListHeading=Optionen:%n
|
||||||
|
|
||||||
|
key-server=Adresse des verifizierenden Schlüsselservers.%nStandardmäßig: 'https://keys.openpgp.org'
|
|
@ -0,0 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Interact with Verifying Key Servers
|
||||||
|
usage.synopsisHeading=Usage:\u0020
|
||||||
|
usage.commandListHeading=Commands:%n
|
|
@ -0,0 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
usage.header=Interagiere mit verifizierenden Schlüsselservern
|
||||||
|
usage.synopsisHeading=Nutzung:\u0020
|
||||||
|
usage.commandListHeading=Befehle:%n
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# VKS-Java
|
||||||
|
|
||||||
|
[![javadoc](https://javadoc.io/badge2/org.pgpainless/vks-java/javadoc.svg)](https://javadoc.io/doc/org.pgpainless/vks-java)
|
||||||
|
[![Maven Central](https://badgen.net/maven/v/maven-central/org.pgpainless/vks-java)](https://search.maven.org/artifact/org.pgpainless/vks-java)
|
||||||
|
|
||||||
|
Client Side API for Communicating with Verifying Key Servers.
|
||||||
|
|
||||||
|
```java
|
||||||
|
VKS vks = new VKSImpl("https://keys.openpgp.org/");
|
||||||
|
|
||||||
|
// Key Discovery via Email, key-id or fingerprint
|
||||||
|
InputStream bobsKey = vks.get().byEmail("bob@pgpainless.org");
|
||||||
|
|
||||||
|
// Upload Key to the VKS
|
||||||
|
InputStream myKey = ...
|
||||||
|
Upload.Response uploadResponse = vks.upload().cert(myKey);
|
||||||
|
|
||||||
|
// Request email verification of user-ids
|
||||||
|
RequestVerify.Response verifyResponse = vks.requestVerification()
|
||||||
|
.forEmailAddress("bob@pgpainless.org")
|
||||||
|
.execute(uploadResponse.getToken());
|
||||||
|
```
|
|
@ -13,23 +13,24 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// JUnit
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
|
|
||||||
// Arrays.areEqual, Base64...
|
// Arrays.areEqual, Base64...
|
||||||
implementation("org.bouncycastle:bcutil-jdk15on:1.70")
|
implementation "org.bouncycastle:bcutil-jdk15to18:$bouncycastleVersion"
|
||||||
|
|
||||||
// @Nonnull, @Nullable...
|
// @Nonnull, @Nullable...
|
||||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
implementation "com.google.code.findbugs:jsr305:$jsrVersion"
|
||||||
|
|
||||||
// Lombok (@SneakyThrows...)
|
// Lombok (@SneakyThrows...)
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.22'
|
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.22'
|
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||||
testCompileOnly 'org.projectlombok:lombok:1.18.22'
|
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
|
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||||
|
|
||||||
// JSON
|
// JSON
|
||||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2'
|
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDataBindVersion"
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
|
@ -9,20 +9,54 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request email verification for email addresses of user-ids on a certificate.
|
||||||
|
*/
|
||||||
public interface RequestVerify {
|
public interface RequestVerify {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request email verification for the given email address.
|
||||||
|
*
|
||||||
|
* @param emailAddress email address
|
||||||
|
* @return builder
|
||||||
|
*/
|
||||||
default ForEmailAddresses forEmailAddress(String emailAddress) {
|
default ForEmailAddresses forEmailAddress(String emailAddress) {
|
||||||
return forEmailAddresses(emailAddress);
|
return forEmailAddresses(emailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request email verification for one or more email addresses.
|
||||||
|
*
|
||||||
|
* @param emailAddresses email addresses
|
||||||
|
* @return builder
|
||||||
|
*/
|
||||||
ForEmailAddresses forEmailAddresses(String... emailAddresses);
|
ForEmailAddresses forEmailAddresses(String... emailAddresses);
|
||||||
|
|
||||||
interface ForEmailAddresses {
|
interface ForEmailAddresses {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the verification request using the given <pre>token</pre>, retrieved earlier by uploading the
|
||||||
|
* certificate to the server.
|
||||||
|
*
|
||||||
|
* @param token access token to authorize the request
|
||||||
|
* @return servers successful response
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an error
|
||||||
|
*/
|
||||||
default Response execute(String token) throws IOException {
|
default Response execute(String token) throws IOException {
|
||||||
return execute(token, Arrays.asList("en_US", "en_GB"));
|
return execute(token, Arrays.asList("en_US", "en_GB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the verification request using the given <pre>token</pre>, retrieved earlier by uploading the
|
||||||
|
* certificate to the server.
|
||||||
|
*
|
||||||
|
* @param token access token to authorize the request
|
||||||
|
* @param locale list of preferred locales for the verification emails
|
||||||
|
* @return servers successful response
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an error
|
||||||
|
*/
|
||||||
Response execute(String token, List<String> locale) throws IOException;
|
Response execute(String token, List<String> locale) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,14 +75,29 @@ public interface RequestVerify {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the uppercase OpenPGP fingerprint of the certificate.
|
||||||
|
*
|
||||||
|
* @return fingerprint
|
||||||
|
*/
|
||||||
public String getKeyFingerprint() {
|
public String getKeyFingerprint() {
|
||||||
return keyFingerprint;
|
return keyFingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a map of {@link Status States} of email addresses of user-ids on the certificate after requesting
|
||||||
|
* verification.
|
||||||
|
* @return status map
|
||||||
|
*/
|
||||||
public Map<String, Status> getStatus() {
|
public Map<String, Status> getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an access token to be used to make further requests to the API.
|
||||||
|
*
|
||||||
|
* @return token
|
||||||
|
*/
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
package pgp.vks.client;
|
package pgp.vks.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum representing different states an email address of a user-id on a certificate can be in.
|
||||||
|
*/
|
||||||
public enum Status {
|
public enum Status {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,6 +27,11 @@ public class VKSImpl implements VKS {
|
||||||
this.api = new URLMapper(vksService);
|
this.api = new URLMapper(vksService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link VKSImpl} instance targeting the <pre>https://keys.openpgp.org</pre> VKS.
|
||||||
|
*
|
||||||
|
* @return VKS instance
|
||||||
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static VKS keysDotOpenPgpDotOrg() {
|
public static VKS keysDotOpenPgpDotOrg() {
|
||||||
return new VKSImpl("https://keys.openpgp.org");
|
return new VKSImpl("https://keys.openpgp.org");
|
||||||
|
|
|
@ -6,6 +6,9 @@ package pgp.vks.client.exception;
|
||||||
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception gets thrown when a public key certificate cannot be published for some reason.
|
||||||
|
*/
|
||||||
public class CertCannotBePublishedException extends ConnectException {
|
public class CertCannotBePublishedException extends ConnectException {
|
||||||
|
|
||||||
public CertCannotBePublishedException(String errorMessage) {
|
public CertCannotBePublishedException(String errorMessage) {
|
||||||
|
|
|
@ -6,12 +6,11 @@ package pgp.vks.client.exception;
|
||||||
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception gets thrown when the queried OpenPGP certificate cannot be found on the server.
|
||||||
|
*/
|
||||||
public class CertNotFoundException extends ConnectException {
|
public class CertNotFoundException extends ConnectException {
|
||||||
|
|
||||||
public CertNotFoundException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CertNotFoundException(String message) {
|
public CertNotFoundException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
|
|
||||||
package pgp.vks.client.exception;
|
package pgp.vks.client.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception gets thrown when an unsupported API {@link pgp.vks.client.VKS.Version} is used.
|
||||||
|
* E.g. user wants to use some command using v2 API, but implementation only supports v1.
|
||||||
|
*/
|
||||||
public class UnsupportedApiException extends RuntimeException {
|
public class UnsupportedApiException extends RuntimeException {
|
||||||
|
|
||||||
public UnsupportedApiException(String message) {
|
public UnsupportedApiException(String message) {
|
||||||
|
|
|
@ -6,6 +6,11 @@ package pgp.vks.client.v1.dto;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error response that gets returned by the server if a POST request fails.
|
||||||
|
*
|
||||||
|
* @see <a href="https://keys.openpgp.org/about/api">VKS API Documentation</a>
|
||||||
|
*/
|
||||||
public class ErrorResponseDto {
|
public class ErrorResponseDto {
|
||||||
|
|
||||||
private final String error;
|
private final String error;
|
||||||
|
@ -14,6 +19,11 @@ public class ErrorResponseDto {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the error message.
|
||||||
|
*
|
||||||
|
* @return error message
|
||||||
|
*/
|
||||||
@JsonProperty("error")
|
@JsonProperty("error")
|
||||||
public String getError() {
|
public String getError() {
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -14,6 +14,11 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for uploading a certificate to the VKS.
|
||||||
|
*
|
||||||
|
* @see <a href="https://keys.openpgp.org/about/api">VKS API Documentation</a>
|
||||||
|
*/
|
||||||
public class UploadRequestDto {
|
public class UploadRequestDto {
|
||||||
|
|
||||||
private static final byte[] ARMOR_HEADER = "-----BEGIN PGP PUBLIC KEY BLOCK-----".getBytes(Charset.forName("UTF8"));
|
private static final byte[] ARMOR_HEADER = "-----BEGIN PGP PUBLIC KEY BLOCK-----".getBytes(Charset.forName("UTF8"));
|
||||||
|
@ -24,6 +29,15 @@ public class UploadRequestDto {
|
||||||
this.keytext = keytext;
|
this.keytext = keytext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link UploadRequestDto} from an ASCII armored or binary OpenPGP certificate which is read from the
|
||||||
|
* given <pre>certInStream</pre>.
|
||||||
|
*
|
||||||
|
* @param certInStream {@link InputStream} containing the ASCII armored or binary OpenPGP certificate
|
||||||
|
* @return request DTO
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an IO error
|
||||||
|
*/
|
||||||
public static UploadRequestDto fromInputStream(InputStream certInStream) throws IOException {
|
public static UploadRequestDto fromInputStream(InputStream certInStream) throws IOException {
|
||||||
ByteArrayOutputStream certBuf = new ByteArrayOutputStream();
|
ByteArrayOutputStream certBuf = new ByteArrayOutputStream();
|
||||||
Streams.pipeAll(certInStream, certBuf);
|
Streams.pipeAll(certInStream, certBuf);
|
||||||
|
@ -31,11 +45,26 @@ public class UploadRequestDto {
|
||||||
return fromBytes(certBuf.toByteArray());
|
return fromBytes(certBuf.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link UploadRequestDto} from an ASCII armored or binary OpenPGP certificate which is read from the
|
||||||
|
* given <pre>keytext</pre> byte array.
|
||||||
|
*
|
||||||
|
* @param keytext byte array containing the ASCII armored or binary OpenPGP certificate
|
||||||
|
* @return request DTO
|
||||||
|
*/
|
||||||
public static UploadRequestDto fromBytes(byte[] keytext) {
|
public static UploadRequestDto fromBytes(byte[] keytext) {
|
||||||
String armoredOrBase64 = new String(base64IfNecessary(keytext));
|
String armoredOrBase64 = new String(base64IfNecessary(keytext));
|
||||||
return new UploadRequestDto(armoredOrBase64);
|
return new UploadRequestDto(armoredOrBase64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a serialized OpenPGP certificate for upload.
|
||||||
|
* If the given <pre>certBytes</pre> contain an ASCII armored OpenPGP certificate, do nothing.
|
||||||
|
* Otherwise, consider the bytes to be the binary representation of an OpenPGP certificate and wrap it in Base64 encoding.
|
||||||
|
*
|
||||||
|
* @param certBytes certificate bytes
|
||||||
|
* @return Unmodified ASCII armored, or base64 encoded binary OpenPGP certificate
|
||||||
|
*/
|
||||||
private static byte[] base64IfNecessary(byte[] certBytes) {
|
private static byte[] base64IfNecessary(byte[] certBytes) {
|
||||||
if (!Arrays.areEqual(certBytes, 0, ARMOR_HEADER.length, ARMOR_HEADER, 0, ARMOR_HEADER.length)) {
|
if (!Arrays.areEqual(certBytes, 0, ARMOR_HEADER.length, ARMOR_HEADER, 0, ARMOR_HEADER.length)) {
|
||||||
certBytes = Base64.encode(certBytes);
|
certBytes = Base64.encode(certBytes);
|
||||||
|
|
|
@ -11,6 +11,11 @@ import pgp.vks.client.Upload;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The VKS servers response to a successful upload.
|
||||||
|
*
|
||||||
|
* @see <a href="https://keys.openpgp.org/about/api">VKS API Documentation</a>
|
||||||
|
*/
|
||||||
public class UploadResponseDto {
|
public class UploadResponseDto {
|
||||||
|
|
||||||
private final String key_fpr;
|
private final String key_fpr;
|
||||||
|
@ -25,21 +30,41 @@ public class UploadResponseDto {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uppercase fingerprint of the uploaded OpenPGP certificate.
|
||||||
|
*
|
||||||
|
* @return fingerprint
|
||||||
|
*/
|
||||||
@JsonProperty("key_fpr")
|
@JsonProperty("key_fpr")
|
||||||
public String getKeyFingerprint() {
|
public String getKeyFingerprint() {
|
||||||
return key_fpr;
|
return key_fpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access token which can be used for a limited time to request verification emails for user-ids on the certificate.
|
||||||
|
*
|
||||||
|
* @return access token
|
||||||
|
*/
|
||||||
@JsonProperty("token")
|
@JsonProperty("token")
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of {@link Status States} of email addresses of user-ids on the certificate.
|
||||||
|
*
|
||||||
|
* @return email status map
|
||||||
|
*/
|
||||||
@JsonProperty("status")
|
@JsonProperty("status")
|
||||||
public Map<String, Status> getStatus() {
|
public Map<String, Status> getStatus() {
|
||||||
return new HashMap<>(status);
|
return new HashMap<>(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the DTO into an entity.
|
||||||
|
*
|
||||||
|
* @return entity
|
||||||
|
*/
|
||||||
public Upload.Response toEntity() {
|
public Upload.Response toEntity() {
|
||||||
return new Upload.Response(getKeyFingerprint(), getStatus(), getToken());
|
return new Upload.Response(getKeyFingerprint(), getStatus(), getToken());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for email verification of one or more unpublished/pending user-ids.
|
||||||
|
*
|
||||||
|
* @see <a href="https://keys.openpgp.org/about/api">VKS API Documentation</a>
|
||||||
|
*/
|
||||||
public class VerificationRequestDto {
|
public class VerificationRequestDto {
|
||||||
|
|
||||||
private final String token;
|
private final String token;
|
||||||
|
@ -22,16 +27,31 @@ public class VerificationRequestDto {
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the token which was previously required by uploading a certificate using an {@link UploadRequestDto}.
|
||||||
|
*
|
||||||
|
* @return access token used to authenticate the request
|
||||||
|
*/
|
||||||
@JsonProperty("token")
|
@JsonProperty("token")
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link List} of email addresses for which to request email verification.
|
||||||
|
*
|
||||||
|
* @return email addresses
|
||||||
|
*/
|
||||||
@JsonProperty("addresses")
|
@JsonProperty("addresses")
|
||||||
public List<String> getAddresses() {
|
public List<String> getAddresses() {
|
||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link List} of preferred locales for the verification mails.
|
||||||
|
*
|
||||||
|
* @return locale list
|
||||||
|
*/
|
||||||
@JsonProperty("locale")
|
@JsonProperty("locale")
|
||||||
public List<String> getLocale() {
|
public List<String> getLocale() {
|
||||||
return locale;
|
return locale;
|
||||||
|
|
|
@ -10,6 +10,11 @@ import pgp.vks.client.Status;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VKS servers response to a successful verification request.
|
||||||
|
*
|
||||||
|
* @see <a href="https://keys.openpgp.org/about/api">VKS API Documentation</a>
|
||||||
|
*/
|
||||||
public class VerificationResponseDto {
|
public class VerificationResponseDto {
|
||||||
|
|
||||||
private final String key_fpr;
|
private final String key_fpr;
|
||||||
|
@ -24,21 +29,41 @@ public class VerificationResponseDto {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uppercase OpenPGP fingerprint of the certificate.
|
||||||
|
*
|
||||||
|
* @return fingerprint
|
||||||
|
*/
|
||||||
@JsonProperty("key_fpr")
|
@JsonProperty("key_fpr")
|
||||||
public String getKeyFingerprint() {
|
public String getKeyFingerprint() {
|
||||||
return key_fpr;
|
return key_fpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access token which can be used to authenticate further verification requests.
|
||||||
|
*
|
||||||
|
* @return token
|
||||||
|
*/
|
||||||
@JsonProperty("token")
|
@JsonProperty("token")
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of {@link Status States} of email addresses of user-ids on the certificate.
|
||||||
|
*
|
||||||
|
* @return email address status map
|
||||||
|
*/
|
||||||
@JsonProperty("status")
|
@JsonProperty("status")
|
||||||
public Map<String, Status> getStatus() {
|
public Map<String, Status> getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this DTO to an entity.
|
||||||
|
*
|
||||||
|
* @return entity
|
||||||
|
*/
|
||||||
public RequestVerify.Response toEntity() {
|
public RequestVerify.Response toEntity() {
|
||||||
return new RequestVerify.Response(getKeyFingerprint(), getStatus(), getToken());
|
return new RequestVerify.Response(getKeyFingerprint(), getStatus(), getToken());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue