mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-26 14:22:05 +01:00
Add KeyRingInfo class that helps to extract information about key rings
This commit is contained in:
parent
5efb303008
commit
cae93022ad
3 changed files with 286 additions and 0 deletions
|
@ -19,6 +19,7 @@ import java.io.IOException;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
@ -27,6 +28,7 @@ import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
import org.pgpainless.encryption_signing.EncryptionBuilder;
|
import org.pgpainless.encryption_signing.EncryptionBuilder;
|
||||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
import org.pgpainless.key.generation.KeyRingBuilder;
|
import org.pgpainless.key.generation.KeyRingBuilder;
|
||||||
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor;
|
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor;
|
||||||
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
||||||
import org.pgpainless.key.parsing.KeyRingReader;
|
import org.pgpainless.key.parsing.KeyRingReader;
|
||||||
|
@ -71,6 +73,16 @@ public class PGPainless {
|
||||||
return new SecretKeyRingEditor(secretKeys);
|
return new SecretKeyRingEditor(secretKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quickly access information about a {@link org.bouncycastle.openpgp.PGPPublicKeyRing} / {@link PGPSecretKeyRing}.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @return access object
|
||||||
|
*/
|
||||||
|
public static KeyRingInfo inspectKeyRing(PGPKeyRing keyRing) {
|
||||||
|
return new KeyRingInfo(keyRing);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt some data symmetrically using OpenPGP and a password.
|
* Encrypt some data symmetrically using OpenPGP and a password.
|
||||||
* The resulting data will be uncompressed and integrity protected.
|
* The resulting data will be uncompressed and integrity protected.
|
||||||
|
|
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.key.info;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||||
|
import org.pgpainless.algorithm.SignatureType;
|
||||||
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to quickly extract certain information from a {@link PGPPublicKeyRing}/{@link PGPSecretKeyRing}.
|
||||||
|
*/
|
||||||
|
public class KeyRingInfo {
|
||||||
|
|
||||||
|
private static final Pattern PATTERN_EMAIL = Pattern.compile("[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}");
|
||||||
|
|
||||||
|
private final PGPKeyRing keys;
|
||||||
|
|
||||||
|
public KeyRingInfo(PGPKeyRing keys) {
|
||||||
|
this.keys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the first {@link PGPPublicKey} of this key ring.
|
||||||
|
*
|
||||||
|
* @return public key
|
||||||
|
*/
|
||||||
|
public PGPPublicKey getPublicKey() {
|
||||||
|
return keys.getPublicKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all {@link PGPPublicKey PGPPublicKeys} of this key ring.
|
||||||
|
* The first key in the list being the primary key.
|
||||||
|
* Note that the list is unmodifiable.
|
||||||
|
*
|
||||||
|
* @return list of public keys
|
||||||
|
*/
|
||||||
|
public List<PGPPublicKey> getPublicKeys() {
|
||||||
|
List<PGPPublicKey> list = new ArrayList<>();
|
||||||
|
Iterator<PGPPublicKey> iterator = keys.getPublicKeys();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
list.add(iterator.next());
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the primary {@link PGPSecretKey} of this key ring or null if the key ring is not a {@link PGPSecretKeyRing}.
|
||||||
|
*
|
||||||
|
* @return primary secret key or null if the key ring is public
|
||||||
|
*/
|
||||||
|
public PGPSecretKey getSecretKey() {
|
||||||
|
if (keys instanceof PGPSecretKeyRing) {
|
||||||
|
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) keys;
|
||||||
|
return secretKeys.getSecretKey();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all secret keys of the key ring.
|
||||||
|
* If the key ring is a {@link PGPPublicKeyRing}, then return an empty list.
|
||||||
|
* Note that the list is unmodifiable.
|
||||||
|
*
|
||||||
|
* @return list of secret keys
|
||||||
|
*/
|
||||||
|
public List<PGPSecretKey> getSecretKeys() {
|
||||||
|
List<PGPSecretKey> list = new ArrayList<>();
|
||||||
|
if (keys instanceof PGPSecretKeyRing) {
|
||||||
|
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) keys;
|
||||||
|
Iterator<PGPSecretKey> iterator = secretKeys.getSecretKeys();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
list.add(iterator.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key id of the primary key of this key ring.
|
||||||
|
*
|
||||||
|
* @return key id
|
||||||
|
*/
|
||||||
|
public long getKeyId() {
|
||||||
|
return getPublicKey().getKeyID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link OpenPgpV4Fingerprint} of this key ring.
|
||||||
|
*
|
||||||
|
* @return fingerprint
|
||||||
|
*/
|
||||||
|
public OpenPgpV4Fingerprint getFingerprint() {
|
||||||
|
return new OpenPgpV4Fingerprint(getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of all user-ids of the primary key.
|
||||||
|
*
|
||||||
|
* @return list of user-ids
|
||||||
|
*/
|
||||||
|
public List<String> getUserIds() {
|
||||||
|
List<String> userIds = new ArrayList<>();
|
||||||
|
Iterator<String> iterator = getPublicKey().getUserIDs();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
userIds.add(iterator.next());
|
||||||
|
}
|
||||||
|
return userIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of all user-ids of the primary key that appear to be email-addresses.
|
||||||
|
*
|
||||||
|
* @return email addresses
|
||||||
|
*/
|
||||||
|
public List<String> getEmailAddresses() {
|
||||||
|
List<String> userIds = getUserIds();
|
||||||
|
List<String> emails = new ArrayList<>();
|
||||||
|
for (String userId : userIds) {
|
||||||
|
Matcher matcher = PATTERN_EMAIL.matcher(userId);
|
||||||
|
if (matcher.find()) {
|
||||||
|
emails.add(matcher.group());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the algorithm of the primary key.
|
||||||
|
*
|
||||||
|
* @return public key algorithm
|
||||||
|
*/
|
||||||
|
public PublicKeyAlgorithm getAlgorithm() {
|
||||||
|
return PublicKeyAlgorithm.fromId(getPublicKey().getAlgorithm());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the creation date of the primary key.
|
||||||
|
*
|
||||||
|
* @return creation date
|
||||||
|
*/
|
||||||
|
public Date getCreationDate() {
|
||||||
|
return getPublicKey().getCreationTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the date on which the key ring was last modified.
|
||||||
|
* This date corresponds to the date of the last signature that was made on this key ring by the primary key.
|
||||||
|
*
|
||||||
|
* @return last modification date.
|
||||||
|
*/
|
||||||
|
public Date getLastModified() {
|
||||||
|
Iterator<PGPSignature> signatures = getPublicKey().getSignatures();
|
||||||
|
long last = 0L;
|
||||||
|
while (signatures.hasNext()) {
|
||||||
|
PGPSignature signature = signatures.next();
|
||||||
|
if (getKeyId() != signature.getKeyID()) {
|
||||||
|
// Throw away signatures made from others
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last = Math.max(last, signature.getCreationTime().getTime());
|
||||||
|
}
|
||||||
|
return new Date(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the date on which the primary key was revoked, or null if it has not yet been revoked.
|
||||||
|
*
|
||||||
|
* @return revocation date or null
|
||||||
|
*/
|
||||||
|
public Date getRevocationDate() {
|
||||||
|
Iterator<PGPSignature> revocations = getPublicKey().getSignaturesOfType(SignatureType.KEY_REVOCATION.getCode());
|
||||||
|
while (revocations.hasNext()) {
|
||||||
|
PGPSignature revocation = revocations.next();
|
||||||
|
if (getKeyId() != revocation.getKeyID()) {
|
||||||
|
// Throw away signatures made from others
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return revocation.getCreationTime();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the date of expiration of the primary key.
|
||||||
|
*
|
||||||
|
* @return expiration date
|
||||||
|
*/
|
||||||
|
public Date getExpirationDate() {
|
||||||
|
long validSeconds = getPublicKey().getValidSeconds();
|
||||||
|
return new Date(getCreationDate().getTime() + (1000 * validSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the key ring is a {@link PGPSecretKeyRing}.
|
||||||
|
* If it is a {@link PGPPublicKeyRing} return false and if it is neither, throw an {@link AssertionError}.
|
||||||
|
*
|
||||||
|
* @return true if the key ring is a secret key ring.
|
||||||
|
*/
|
||||||
|
public boolean isSecretKey() {
|
||||||
|
if (keys instanceof PGPSecretKeyRing) {
|
||||||
|
return true;
|
||||||
|
} else if (keys instanceof PGPPublicKeyRing) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Expected PGPKeyRing to be either PGPPublicKeyRing or PGPSecretKeyRing, but got " + keys.getClass().getName() + " instead.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true when every secret key on the key ring is not encrypted.
|
||||||
|
* If there is at least one encrypted secret key on the ring, return false.
|
||||||
|
* If the ring is a {@link PGPPublicKeyRing}, return true.
|
||||||
|
*
|
||||||
|
* @return true if all secret keys are unencrypted.
|
||||||
|
*/
|
||||||
|
public boolean isFullyDecrypted() {
|
||||||
|
if (isSecretKey()) {
|
||||||
|
for (PGPSecretKey secretKey : getSecretKeys()) {
|
||||||
|
if (secretKey.getKeyEncryptionAlgorithm() != SymmetricKeyAlgorithm.NULL.getAlgorithmId()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Extract information from PGPKeyRings.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.key.info;
|
Loading…
Reference in a new issue