mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 11:27:57 +01:00
Annotate and document ArmorUtils class
This commit is contained in:
parent
e89e0f216c
commit
16b0d0730e
2 changed files with 367 additions and 100 deletions
|
@ -31,6 +31,9 @@ import org.bouncycastle.util.io.Streams;
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class ArmorUtils {
|
public final class ArmorUtils {
|
||||||
|
|
||||||
// MessageIDs are 32 printable characters
|
// MessageIDs are 32 printable characters
|
||||||
|
@ -46,27 +49,79 @@ public final class ArmorUtils {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toAsciiArmoredString(PGPSecretKey secretKey) throws IOException {
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given {@link PGPSecretKey}.
|
||||||
|
*
|
||||||
|
* @param secretKey secret key
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull PGPSecretKey secretKey)
|
||||||
|
throws IOException {
|
||||||
MultiMap<String, String> header = keyToHeader(secretKey.getPublicKey());
|
MultiMap<String, String> header = keyToHeader(secretKey.getPublicKey());
|
||||||
return toAsciiArmoredString(secretKey.getEncoded(), header);
|
return toAsciiArmoredString(secretKey.getEncoded(), header);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toAsciiArmoredString(PGPPublicKey publicKey) throws IOException {
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given {@link PGPPublicKey}.
|
||||||
|
*
|
||||||
|
* @param publicKey public key
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull PGPPublicKey publicKey)
|
||||||
|
throws IOException {
|
||||||
MultiMap<String, String> header = keyToHeader(publicKey);
|
MultiMap<String, String> header = keyToHeader(publicKey);
|
||||||
return toAsciiArmoredString(publicKey.getEncoded(), header);
|
return toAsciiArmoredString(publicKey.getEncoded(), header);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toAsciiArmoredString(PGPSecretKeyRing secretKeys) throws IOException {
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given {@link PGPSecretKeyRing}.
|
||||||
|
*
|
||||||
|
* @param secretKeys secret key ring
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull PGPSecretKeyRing secretKeys)
|
||||||
|
throws IOException {
|
||||||
MultiMap<String, String> header = keysToHeader(secretKeys);
|
MultiMap<String, String> header = keysToHeader(secretKeys);
|
||||||
return toAsciiArmoredString(secretKeys.getEncoded(), header);
|
return toAsciiArmoredString(secretKeys.getEncoded(), header);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toAsciiArmoredString(PGPPublicKeyRing publicKeys) throws IOException {
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given {@link PGPPublicKeyRing}.
|
||||||
|
*
|
||||||
|
* @param publicKeys public key ring
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull PGPPublicKeyRing publicKeys)
|
||||||
|
throws IOException {
|
||||||
MultiMap<String, String> header = keysToHeader(publicKeys);
|
MultiMap<String, String> header = keysToHeader(publicKeys);
|
||||||
return toAsciiArmoredString(publicKeys.getEncoded(), header);
|
return toAsciiArmoredString(publicKeys.getEncoded(), header);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toAsciiArmoredString(PGPSecretKeyRingCollection secretKeyRings) throws IOException {
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given {@link PGPSecretKeyRingCollection}.
|
||||||
|
* The encoding will use per-key ASCII armors protecting each {@link PGPSecretKeyRing} individually.
|
||||||
|
* Those armors are then concatenated with newlines in between.
|
||||||
|
*
|
||||||
|
* @param secretKeyRings secret key ring collection
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull PGPSecretKeyRingCollection secretKeyRings)
|
||||||
|
throws IOException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (Iterator<PGPSecretKeyRing> iterator = secretKeyRings.iterator(); iterator.hasNext(); ) {
|
for (Iterator<PGPSecretKeyRing> iterator = secretKeyRings.iterator(); iterator.hasNext(); ) {
|
||||||
PGPSecretKeyRing secretKeyRing = iterator.next();
|
PGPSecretKeyRing secretKeyRing = iterator.next();
|
||||||
|
@ -78,24 +133,19 @@ public final class ArmorUtils {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArmoredOutputStream toAsciiArmoredStream(PGPKeyRing keyRing, OutputStream outputStream) {
|
/**
|
||||||
MultiMap<String, String> header = keysToHeader(keyRing);
|
* Return the ASCII armored encoding of the given {@link PGPPublicKeyRingCollection}.
|
||||||
return toAsciiArmoredStream(outputStream, header);
|
* The encoding will use per-key ASCII armors protecting each {@link PGPPublicKeyRing} individually.
|
||||||
}
|
* Those armors are then concatenated with newlines in between.
|
||||||
|
*
|
||||||
public static ArmoredOutputStream toAsciiArmoredStream(OutputStream outputStream, MultiMap<String, String> header) {
|
* @param publicKeyRings public key ring collection
|
||||||
ArmoredOutputStream armoredOutputStream = ArmoredOutputStreamFactory.get(outputStream);
|
* @return ascii armored encoding
|
||||||
if (header != null) {
|
*
|
||||||
for (String headerKey : header.keySet()) {
|
* @throws IOException in case of an io error
|
||||||
for (String headerValue : header.get(headerKey)) {
|
*/
|
||||||
armoredOutputStream.addHeader(headerKey, headerValue);
|
@Nonnull
|
||||||
}
|
public static String toAsciiArmoredString(@Nonnull PGPPublicKeyRingCollection publicKeyRings)
|
||||||
}
|
throws IOException {
|
||||||
}
|
|
||||||
return armoredOutputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toAsciiArmoredString(PGPPublicKeyRingCollection publicKeyRings) throws IOException {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (Iterator<PGPPublicKeyRing> iterator = publicKeyRings.iterator(); iterator.hasNext(); ) {
|
for (Iterator<PGPPublicKeyRing> iterator = publicKeyRings.iterator(); iterator.hasNext(); ) {
|
||||||
PGPPublicKeyRing publicKeyRing = iterator.next();
|
PGPPublicKeyRing publicKeyRing = iterator.next();
|
||||||
|
@ -107,7 +157,204 @@ public final class ArmorUtils {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tuple<String, Integer> getPrimaryUserIdAndUserIdCount(PGPPublicKey publicKey) {
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given OpenPGP data bytes.
|
||||||
|
*
|
||||||
|
* @param bytes openpgp data
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull byte[] bytes)
|
||||||
|
throws IOException {
|
||||||
|
return toAsciiArmoredString(bytes, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ASCII armored encoding of the given OpenPGP data bytes.
|
||||||
|
* The ASCII armor will include headers from the header map.
|
||||||
|
*
|
||||||
|
* @param bytes OpenPGP data
|
||||||
|
* @param additionalHeaderValues header map
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull byte[] bytes,
|
||||||
|
@Nullable MultiMap<String, String> additionalHeaderValues)
|
||||||
|
throws IOException {
|
||||||
|
return toAsciiArmoredString(new ByteArrayInputStream(bytes), additionalHeaderValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ASCII armored encoding of the {@link InputStream} containing OpenPGP data.
|
||||||
|
*
|
||||||
|
* @param inputStream input stream of OpenPGP data
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull InputStream inputStream)
|
||||||
|
throws IOException {
|
||||||
|
return toAsciiArmoredString(inputStream, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ASCII armored encoding of the OpenPGP data from the given {@link InputStream}.
|
||||||
|
* The ASCII armor will include armor headers from the given header map.
|
||||||
|
*
|
||||||
|
* @param inputStream input stream of OpenPGP data
|
||||||
|
* @param additionalHeaderValues ASCII armor header map
|
||||||
|
* @return ASCII armored encoding
|
||||||
|
*
|
||||||
|
* @throws IOException in case of an io error
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static String toAsciiArmoredString(@Nonnull InputStream inputStream,
|
||||||
|
@Nullable MultiMap<String, String> additionalHeaderValues)
|
||||||
|
throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ArmoredOutputStream armor = toAsciiArmoredStream(out, additionalHeaderValues);
|
||||||
|
Streams.pipeAll(inputStream, armor);
|
||||||
|
armor.close();
|
||||||
|
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an {@link ArmoredOutputStream} prepared with headers for the given key ring, which wraps the given
|
||||||
|
* {@link OutputStream}.
|
||||||
|
*
|
||||||
|
* The armored output stream can be used to encode the key ring by calling {@link PGPKeyRing#encode(OutputStream)}
|
||||||
|
* with the armored output stream as an argument.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param outputStream wrapped output stream
|
||||||
|
* @return armored output stream
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static ArmoredOutputStream toAsciiArmoredStream(@Nonnull PGPKeyRing keyRing,
|
||||||
|
@Nonnull OutputStream outputStream) {
|
||||||
|
MultiMap<String, String> header = keysToHeader(keyRing);
|
||||||
|
return toAsciiArmoredStream(outputStream, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link ArmoredOutputStream} wrapping the given {@link OutputStream}.
|
||||||
|
* The armored output stream will be prepared with armor headers given by header.
|
||||||
|
*
|
||||||
|
* Note: Since the armored output stream is retrieved from {@link ArmoredOutputStreamFactory#get(OutputStream)},
|
||||||
|
* it may already come with custom headers. Hence, the header entries given by header are appended below those
|
||||||
|
* already populated headers.
|
||||||
|
*
|
||||||
|
* @param outputStream output stream to wrap
|
||||||
|
* @param header map of header entries
|
||||||
|
* @return armored output stream
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static ArmoredOutputStream toAsciiArmoredStream(@Nonnull OutputStream outputStream,
|
||||||
|
@Nullable MultiMap<String, String> header) {
|
||||||
|
ArmoredOutputStream armoredOutputStream = ArmoredOutputStreamFactory.get(outputStream);
|
||||||
|
if (header != null) {
|
||||||
|
for (String headerKey : header.keySet()) {
|
||||||
|
for (String headerValue : header.get(headerKey)) {
|
||||||
|
armoredOutputStream.addHeader(headerKey, headerValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return armoredOutputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an {@link ArmoredOutputStream} prepared with headers for the given key ring, which wraps the given
|
||||||
|
* {@link OutputStream}.
|
||||||
|
*
|
||||||
|
* The armored output stream can be used to encode the key ring by calling {@link PGPKeyRing#encode(OutputStream)}
|
||||||
|
* with the armored output stream as an argument.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param outputStream wrapped output stream
|
||||||
|
* @return armored output stream
|
||||||
|
*
|
||||||
|
* @deprecated use {@link #toAsciiArmoredStream(PGPKeyRing, OutputStream)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Nonnull
|
||||||
|
public static ArmoredOutputStream createArmoredOutputStreamFor(@Nonnull PGPKeyRing keyRing,
|
||||||
|
@Nonnull OutputStream outputStream) {
|
||||||
|
return toAsciiArmoredStream(keyRing, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a header map for ASCII armor from the given {@link PGPKeyRing}.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @return header map
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static MultiMap<String, String> keysToHeader(@Nonnull PGPKeyRing keyRing) {
|
||||||
|
PGPPublicKey publicKey = keyRing.getPublicKey();
|
||||||
|
return keyToHeader(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a header map for ASCII armor from the given {@link PGPPublicKey}.
|
||||||
|
* The header map consists of a comment field of the keys pretty-printed fingerprint,
|
||||||
|
* as well as some optional user-id information (see {@link #setUserIdInfoOnHeader(MultiMap, PGPPublicKey)}.
|
||||||
|
*
|
||||||
|
* @param publicKey public key
|
||||||
|
* @return header map
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static MultiMap<String, String> keyToHeader(@Nonnull PGPPublicKey publicKey) {
|
||||||
|
MultiMap<String, String> header = new MultiMap<>();
|
||||||
|
OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(publicKey);
|
||||||
|
|
||||||
|
header.put(HEADER_COMMENT, fingerprint.prettyPrint());
|
||||||
|
setUserIdInfoOnHeader(header, publicKey);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add user-id information to the header map.
|
||||||
|
* If the key is carrying at least one user-id, we add a comment for the probable primary user-id.
|
||||||
|
* If the key carries more than one user-id, we further add a comment stating how many further identities
|
||||||
|
* the key has.
|
||||||
|
*
|
||||||
|
* @param header header map
|
||||||
|
* @param publicKey public key
|
||||||
|
*/
|
||||||
|
private static void setUserIdInfoOnHeader(@Nonnull MultiMap<String, String> header,
|
||||||
|
@Nonnull PGPPublicKey publicKey) {
|
||||||
|
Tuple<String, Integer> idCount = getPrimaryUserIdAndUserIdCount(publicKey);
|
||||||
|
String primary = idCount.getA();
|
||||||
|
int totalCount = idCount.getB();
|
||||||
|
if (primary != null) {
|
||||||
|
header.put(HEADER_COMMENT, primary);
|
||||||
|
}
|
||||||
|
if (totalCount == 2) {
|
||||||
|
header.put(HEADER_COMMENT, "1 further identity");
|
||||||
|
} else if (totalCount > 2) {
|
||||||
|
header.put(HEADER_COMMENT, String.format("%d further identities", totalCount - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine a probable primary user-id, as well as the total number of user-ids on the given {@link PGPPublicKey}.
|
||||||
|
* This method is trimmed for efficiency and does not do any cryptographic validation of signatures.
|
||||||
|
*
|
||||||
|
* The key might not have any user-id at all, in which case {@link Tuple#getA()} will return null.
|
||||||
|
* The key might have some user-ids, but none of it marked as primary, in which case {@link Tuple#getA()}
|
||||||
|
* will return the first user-id of the key.
|
||||||
|
*
|
||||||
|
* @param publicKey public key
|
||||||
|
* @return tuple consisting of a primary user-id candidate, and the total number of user-ids on the key.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static Tuple<String, Integer> getPrimaryUserIdAndUserIdCount(@Nonnull PGPPublicKey publicKey) {
|
||||||
// Quickly determine the primary user-id + number of total user-ids
|
// Quickly determine the primary user-id + number of total user-ids
|
||||||
// NOTE: THIS METHOD DOES NOT CRYPTOGRAPHICALLY VERIFY THE SIGNATURES
|
// NOTE: THIS METHOD DOES NOT CRYPTOGRAPHICALLY VERIFY THE SIGNATURES
|
||||||
// DO NOT RELY ON IT!
|
// DO NOT RELY ON IT!
|
||||||
|
@ -140,98 +387,93 @@ public final class ArmorUtils {
|
||||||
return new Tuple<>(printed, countIdentities);
|
return new Tuple<>(printed, countIdentities);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MultiMap<String, String> keyToHeader(PGPPublicKey publicKey) {
|
/**
|
||||||
MultiMap<String, String> header = new MultiMap<>();
|
* Add an ASCII armor header entry about the used hash algorithm into the {@link ArmoredOutputStream}.
|
||||||
OpenPgpFingerprint fingerprint = OpenPgpFingerprint.of(publicKey);
|
*
|
||||||
|
* @param armor armored output stream
|
||||||
header.put(HEADER_COMMENT, fingerprint.prettyPrint());
|
* @param hashAlgorithm hash algorithm
|
||||||
setUserIdInfoOnHeader(header, publicKey);
|
*
|
||||||
return header;
|
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-6.2">
|
||||||
}
|
* RFC 4880 - OpenPGP Message Format §6.2. Forming ASCII Armor</a>
|
||||||
|
*/
|
||||||
private static void setUserIdInfoOnHeader(MultiMap<String, String> header, PGPPublicKey publicKey) {
|
public static void addHashAlgorithmHeader(@Nonnull ArmoredOutputStream armor,
|
||||||
Tuple<String, Integer> idCount = getPrimaryUserIdAndUserIdCount(publicKey);
|
@Nonnull HashAlgorithm hashAlgorithm) {
|
||||||
String primary = idCount.getA();
|
|
||||||
int totalCount = idCount.getB();
|
|
||||||
if (primary != null) {
|
|
||||||
header.put(HEADER_COMMENT, primary);
|
|
||||||
}
|
|
||||||
if (totalCount == 2) {
|
|
||||||
header.put(HEADER_COMMENT, "1 further identity");
|
|
||||||
} else if (totalCount > 2) {
|
|
||||||
header.put(HEADER_COMMENT, String.format("%d further identities", totalCount - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MultiMap<String, String> keysToHeader(PGPKeyRing keyRing) {
|
|
||||||
PGPPublicKey publicKey = keyRing.getPublicKey();
|
|
||||||
return keyToHeader(publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toAsciiArmoredString(byte[] bytes) throws IOException {
|
|
||||||
return toAsciiArmoredString(bytes, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toAsciiArmoredString(byte[] bytes, MultiMap<String, String> additionalHeaderValues) throws IOException {
|
|
||||||
return toAsciiArmoredString(new ByteArrayInputStream(bytes), additionalHeaderValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toAsciiArmoredString(InputStream inputStream) throws IOException {
|
|
||||||
return toAsciiArmoredString(inputStream, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addHashAlgorithmHeader(ArmoredOutputStream armor, HashAlgorithm hashAlgorithm) {
|
|
||||||
armor.addHeader(HEADER_HASH, hashAlgorithm.getAlgorithmName());
|
armor.addHeader(HEADER_HASH, hashAlgorithm.getAlgorithmName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addCommentHeader(ArmoredOutputStream armor, String comment) {
|
/**
|
||||||
|
* Add an ASCII armor comment header entry into the {@link ArmoredOutputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored output stream
|
||||||
|
* @param comment free-text comment
|
||||||
|
*
|
||||||
|
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-6.2">
|
||||||
|
* RFC 4880 - OpenPGP Message Format §6.2. Forming ASCII Armor</a>
|
||||||
|
*/
|
||||||
|
public static void addCommentHeader(@Nonnull ArmoredOutputStream armor,
|
||||||
|
@Nonnull String comment) {
|
||||||
armor.addHeader(HEADER_COMMENT, comment);
|
armor.addHeader(HEADER_COMMENT, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addMessageIdHeader(ArmoredOutputStream armor, String messageId) {
|
/**
|
||||||
if (messageId == null) {
|
* Add an ASCII armor message-id header entry into the {@link ArmoredOutputStream}.
|
||||||
throw new NullPointerException("MessageID cannot be null.");
|
*
|
||||||
}
|
* @param armor armored output stream
|
||||||
|
* @param messageId message id
|
||||||
|
*
|
||||||
|
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-6.2">
|
||||||
|
* RFC 4880 - OpenPGP Message Format §6.2. Forming ASCII Armor</a>
|
||||||
|
*/
|
||||||
|
public static void addMessageIdHeader(@Nonnull ArmoredOutputStream armor,
|
||||||
|
@Nonnull String messageId) {
|
||||||
if (!PATTERN_MESSAGE_ID.matcher(messageId).matches()) {
|
if (!PATTERN_MESSAGE_ID.matcher(messageId).matches()) {
|
||||||
throw new IllegalArgumentException("MessageIDs MUST consist of 32 printable characters.");
|
throw new IllegalArgumentException("MessageIDs MUST consist of 32 printable characters.");
|
||||||
}
|
}
|
||||||
armor.addHeader(HEADER_MESSAGEID, messageId);
|
armor.addHeader(HEADER_MESSAGEID, messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toAsciiArmoredString(InputStream inputStream, MultiMap<String, String> additionalHeaderValues) throws IOException {
|
/**
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
* Extract all ASCII armor header values of type comment from the given {@link ArmoredInputStream}.
|
||||||
ArmoredOutputStream armor = toAsciiArmoredStream(out, additionalHeaderValues);
|
*
|
||||||
Streams.pipeAll(inputStream, armor);
|
* @param armor armored input stream
|
||||||
armor.close();
|
* @return list of comment headers
|
||||||
|
*/
|
||||||
return out.toString();
|
@Nonnull
|
||||||
}
|
public static List<String> getCommentHeaderValues(@Nonnull ArmoredInputStream armor) {
|
||||||
|
|
||||||
public static ArmoredOutputStream createArmoredOutputStreamFor(PGPKeyRing keyRing, OutputStream outputStream) {
|
|
||||||
ArmoredOutputStream armor = ArmoredOutputStreamFactory.get(outputStream);
|
|
||||||
MultiMap<String, String> headerMap = keysToHeader(keyRing);
|
|
||||||
for (String header : headerMap.keySet()) {
|
|
||||||
for (String value : headerMap.get(header)) {
|
|
||||||
armor.addHeader(header, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return armor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> getCommentHeaderValues(ArmoredInputStream armor) {
|
|
||||||
return getArmorHeaderValues(armor, HEADER_COMMENT);
|
return getArmorHeaderValues(armor, HEADER_COMMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getMessageIdHeaderValues(ArmoredInputStream armor) {
|
/**
|
||||||
|
* Extract all ASCII armor header values of type message id from the given {@link ArmoredInputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored input stream
|
||||||
|
* @return list of message-id headers
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static List<String> getMessageIdHeaderValues(@Nonnull ArmoredInputStream armor) {
|
||||||
return getArmorHeaderValues(armor, HEADER_MESSAGEID);
|
return getArmorHeaderValues(armor, HEADER_MESSAGEID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getHashHeaderValues(ArmoredInputStream armor) {
|
/**
|
||||||
|
* Return all ASCII armor header values of type hash-algorithm from the given {@link ArmoredInputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored input stream
|
||||||
|
* @return list of hash headers
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static List<String> getHashHeaderValues(@Nonnull ArmoredInputStream armor) {
|
||||||
return getArmorHeaderValues(armor, HEADER_HASH);
|
return getArmorHeaderValues(armor, HEADER_HASH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<HashAlgorithm> getHashAlgorithms(ArmoredInputStream armor) {
|
/**
|
||||||
|
* Return a list of {@link HashAlgorithm} enums extracted from the hash header entries of the given
|
||||||
|
* {@link ArmoredInputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored input stream
|
||||||
|
* @return list of hash algorithms from the ASCII header
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static List<HashAlgorithm> getHashAlgorithms(@Nonnull ArmoredInputStream armor) {
|
||||||
List<String> algorithmNames = getHashHeaderValues(armor);
|
List<String> algorithmNames = getHashHeaderValues(armor);
|
||||||
List<HashAlgorithm> algorithms = new ArrayList<>();
|
List<HashAlgorithm> algorithms = new ArrayList<>();
|
||||||
for (String name : algorithmNames) {
|
for (String name : algorithmNames) {
|
||||||
|
@ -243,15 +485,38 @@ public final class ArmorUtils {
|
||||||
return algorithms;
|
return algorithms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getVersionHeaderValues(ArmoredInputStream armor) {
|
/**
|
||||||
|
* Return all ASCII armor header values of type version from the given {@link ArmoredInputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored input stream
|
||||||
|
* @return list of version headers
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static List<String> getVersionHeaderValues(@Nonnull ArmoredInputStream armor) {
|
||||||
return getArmorHeaderValues(armor, HEADER_VERSION);
|
return getArmorHeaderValues(armor, HEADER_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getCharsetHeaderValues(ArmoredInputStream armor) {
|
/**
|
||||||
|
* Return all ASCII armor header values of type charset from the given {@link ArmoredInputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored input stream
|
||||||
|
* @return list of charset headers
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static List<String> getCharsetHeaderValues(@Nonnull ArmoredInputStream armor) {
|
||||||
return getArmorHeaderValues(armor, HEADER_CHARSET);
|
return getArmorHeaderValues(armor, HEADER_CHARSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getArmorHeaderValues(ArmoredInputStream armor, String headerKey) {
|
/**
|
||||||
|
* Return all ASCII armor header values of the given headerKey from the given {@link ArmoredInputStream}.
|
||||||
|
*
|
||||||
|
* @param armor armored input stream
|
||||||
|
* @param headerKey ASCII armor header key
|
||||||
|
* @return list of values for the header key
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static List<String> getArmorHeaderValues(@Nonnull ArmoredInputStream armor,
|
||||||
|
@Nonnull String headerKey) {
|
||||||
String[] header = armor.getArmorHeaders();
|
String[] header = armor.getArmorHeaders();
|
||||||
String key = headerKey + ": ";
|
String key = headerKey + ": ";
|
||||||
List<String> values = new ArrayList<>();
|
List<String> values = new ArrayList<>();
|
||||||
|
@ -278,7 +543,9 @@ public final class ArmorUtils {
|
||||||
* @param inputStream input stream
|
* @param inputStream input stream
|
||||||
* @return BufferedInputStreamExt
|
* @return BufferedInputStreamExt
|
||||||
*/
|
*/
|
||||||
public static InputStream getDecoderStream(InputStream inputStream) throws IOException {
|
@Nonnull
|
||||||
|
public static InputStream getDecoderStream(@Nonnull InputStream inputStream)
|
||||||
|
throws IOException {
|
||||||
BufferedInputStream buf = new BufferedInputStream(inputStream, 512);
|
BufferedInputStream buf = new BufferedInputStream(inputStream, 512);
|
||||||
InputStream decoderStream = PGPUtilWrapper.getDecoderStream(buf);
|
InputStream decoderStream = PGPUtilWrapper.getDecoderStream(buf);
|
||||||
// Data is not armored -> return
|
// Data is not armored -> return
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class ExtractCertImpl implements ExtractCert {
|
||||||
public void writeTo(OutputStream outputStream) throws IOException {
|
public void writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
|
||||||
for (PGPPublicKeyRing cert : certs) {
|
for (PGPPublicKeyRing cert : certs) {
|
||||||
OutputStream out = armor ? ArmorUtils.createArmoredOutputStreamFor(cert, outputStream) : outputStream;
|
OutputStream out = armor ? ArmorUtils.toAsciiArmoredStream(cert, outputStream) : outputStream;
|
||||||
cert.encode(out);
|
cert.encode(out);
|
||||||
|
|
||||||
if (armor) {
|
if (armor) {
|
||||||
|
|
Loading…
Reference in a new issue