package com.wesabe.grendel.openpgp;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import com.google.common.collect.ImmutableSet;
import com.wesabe.grendel.util.IntegerEquivalents;
import com.wesabe.grendel.util.Iterators;
/**
* An abstract base class for asymmetric PGP public keys and their corresponding
* secret keys.
*
* @author coda
*/
public abstract class AbstractKey {
protected final PGPSecretKey secretKey;
protected final PGPPublicKey publicKey;
protected final KeySignature signature;
protected final Set<KeyFlag> flags;
/**
* Instatiates a new {@link AbstractKey}.
*
* @param key the PGP secret key, with public key included
* @param signingKey the signing key
* @param requiredSignatureType the type of signature required
*/
protected AbstractKey(PGPSecretKey key, PGPSecretKey signingKey,
SignatureType requiredSignatureType) {
this.secretKey = key;
this.publicKey = secretKey.getPublicKey();
this.signature = getSignature(signingKey, requiredSignatureType);
if (signature == null) {
this.flags = ImmutableSet.of();
} else {
this.flags = signature.getKeyFlags();
}
}
/**
* Given the key's passphrase, unlocks the secret key and returns an
* {@link UnlockedKey} equivalent of {@code this}.
*
* @param passphrase the key's passphrase
* @return a {@link UnlockedKey} equivalent of {@code this}
* @throws CryptographicException if {@code passphrase} is incorrect
*/
public abstract UnlockedKey unlock(char[] passphrase) throws CryptographicException;
/**
* Returns this key's public key component.
*/
/* default */ PGPPublicKey getPublicKey() {
return publicKey;
}
/**
* Returns this key's secret key component.
* @return
*/
/* default */ PGPSecretKey getSecretKey() {
return secretKey;
}
/**
* Returns this key's user ID, usually in the form of
* {@code First Last <email@example.com>}.
*/
public String getUserID() {
return getUserIDs().get(0);
}
/**
* Returns a list of all user IDs attached to this key.
*
* @see #getUserID()
*/
public List<String> getUserIDs() {
return Iterators.toList(secretKey.getUserIDs());
}
/**
* Returns the key's ID.
*/
public long getKeyID() {
return secretKey.getKeyID();
}
/**
* Returns a human-readable version of {@link #getKeyID()}.
*
* <b>N.B.:</b> This returns a truncated version of the key ID.
*/
public String getHumanKeyID() {
return String.format("%08X", (int) secretKey.getKeyID());
}
/**
* Returns the key's {@link AsymmetricAlgorithm}.
*/
public AsymmetricAlgorithm getAlgorithm() {
return IntegerEquivalents.fromInt(
AsymmetricAlgorithm.class,
publicKey.getAlgorithm()
);
}
/**
* Returns the key's size, in bits.
*/
public int getSize() {
return publicKey.getBitStrength();
}
/**
* Returns {@code true} if this key can be used to encrypt or decrypt data.
*/
public boolean canEncrypt() {
return flags.contains(KeyFlag.ENCRYPTION);
}
/**
* Returns {@code true} if this key can be used to sign or verify data.
*/
public boolean canSign() {
return flags.contains(KeyFlag.SIGNING);
}
/**
* Returns the date and time at which the key was created.
*/
public DateTime getCreatedAt() {
return new DateTime(publicKey.getCreationTime(), DateTimeZone.UTC);
}
/**
* Returns a list of preferred {@link SymmetricAlgorithm}s for this key.
*/
public List<SymmetricAlgorithm> getPreferredSymmetricAlgorithms() {
return signature.getPreferredSymmetricAlgorithms();
}
/**
* Returns a list of preferred {@link CompressionAlgorithm}s for this key.
*/
public List<CompressionAlgorithm> getPreferredCompressionAlgorithms() {
return signature.getPreferredCompressionAlgorithms();
}
/**
* Returns a list of preferred {@link HashAlgorithm}s for this key.
*/
public List<HashAlgorithm> getPreferredHashAlgorithms() {
return signature.getPreferredHashAlgorithms();
}
@Override
public String toString() {
return String.format(
"%d-%s/%s",
getSize(), getAlgorithm(), getHumanKeyID()
);
}
/**
* Returns a list of {@link KeyFlag}s associated with this key.
*/
public Set<KeyFlag> getKeyFlags() {
return flags;
}
private KeySignature getSignature(PGPSecretKey signingKey,
SignatureType requiredSignatureType) {
final Iterator<?> signatures = publicKey.getSignatures();
while (signatures.hasNext()) {
final KeySignature signature = new KeySignature(
(PGPSignature) signatures.next()
);
if ((signature.getKeyID() == signingKey.getKeyID())
&& (signature.getSignatureType() == requiredSignatureType)) {
return signature;
}
}
return null;
}
}