Skip to content

Instantly share code, notes, and snippets.

@kbsriram
Last active December 18, 2015 04:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kbsriram/5725725 to your computer and use it in GitHub Desktop.
Save kbsriram/5725725 to your computer and use it in GitHub Desktop.
package org.ppdrive.crypto;
import java.io.File;
import java.io.FileInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import org.bouncyrattle.bcpg.ArmoredOutputStream;
import org.bouncyrattle.bcpg.HashAlgorithmTags;
import org.bouncyrattle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncyrattle.bcpg.sig.Features;
import org.bouncyrattle.bcpg.sig.KeyFlags;
import org.bouncyrattle.crypto.DataLengthException;
import org.bouncyrattle.crypto.generators.RSAKeyPairGenerator;
import org.bouncyrattle.crypto.params.RSAKeyGenerationParameters;
import org.bouncyrattle.openpgp.CStrictKeyParser;
import org.bouncyrattle.openpgp.PGPCompressedData;
import org.bouncyrattle.openpgp.PGPCompressedDataGenerator;
import org.bouncyrattle.openpgp.PGPEncryptedData;
import org.bouncyrattle.openpgp.PGPEncryptedDataGenerator;
import org.bouncyrattle.openpgp.PGPEncryptedDataList;
import org.bouncyrattle.openpgp.PGPException;
import org.bouncyrattle.openpgp.PGPKeyPair;
import org.bouncyrattle.openpgp.PGPKeyRingGenerator;
import org.bouncyrattle.openpgp.PGPLiteralData;
import org.bouncyrattle.openpgp.PGPLiteralDataGenerator;
import org.bouncyrattle.openpgp.PGPObjectFactory;
import org.bouncyrattle.openpgp.PGPOnePassSignature;
import org.bouncyrattle.openpgp.PGPOnePassSignatureList;
import org.bouncyrattle.openpgp.PGPPrivateKey;
import org.bouncyrattle.openpgp.PGPPublicKey;
import org.bouncyrattle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncyrattle.openpgp.PGPPublicKeyRing;
import org.bouncyrattle.openpgp.PGPSecretKey;
import org.bouncyrattle.openpgp.PGPSecretKeyRing;
import org.bouncyrattle.openpgp.PGPSignature;
import org.bouncyrattle.openpgp.PGPSignatureGenerator;
import org.bouncyrattle.openpgp.PGPSignatureList;
import org.bouncyrattle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncyrattle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncyrattle.openpgp.operator.PGPDigestCalculator;
import org.bouncyrattle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncyrattle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncyrattle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
import org.bouncyrattle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncyrattle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncyrattle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncyrattle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncyrattle.openpgp.operator.bc.BcPGPKeyPair;
import org.bouncyrattle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
public class CPGPUtil
{
// fullkey means ensure there's also an encryption subkey.
public final static PGPPublicKeyRing readPublicKeyRing
(InputStream inp, boolean fullkey)
throws IOException, PGPException, SignatureException
{ return readPublicKeyRing(inp, fullkey, null); }
// certifying_key not-null means expect a good signature
// on the master key's uid, signed by the certifying_key
@SuppressWarnings("unchecked")
public final static PGPPublicKeyRing readPublicKeyRing
(InputStream inp, boolean fullkey, PGPPublicKey certifying_key)
throws IOException, PGPException, SignatureException
{
// This uses low-level APIs to parse a strict subset of keys
// that we're willing to accept.
PGPPublicKeyRing pkr = CStrictKeyParser.parsePublicKeyRing
(inp, fullkey, certifying_key != null);
// 1) Check we have a single master key, and that it has a
// valid self-signature.
PGPPublicKey master = null;
for (Iterator<PGPPublicKey> pki = pkr.getPublicKeys();
pki.hasNext();) {
PGPPublicKey cur = pki.next();
if (cur.isMasterKey()) {
if (master == null) {
checkMasterKey(cur, certifying_key);
master = cur;
}
else {
throw new PGPException("multiple master keys");
}
}
}
if (master == null) {
throw new PGPException("no master key found");
}
// Every subkey must be signed by the master key.
for (Iterator<PGPPublicKey> pki = pkr.getPublicKeys();
pki.hasNext();) {
PGPPublicKey cur = pki.next();
if (!cur.isMasterKey()) {
checkSubKey(cur, master);
}
}
return pkr;
}
public final static boolean matchFingerprint
(PGPPublicKey pk, String fingerprint)
{
if ((fingerprint == null) || (fingerprint.length() != 40)) {
return false;
}
char[] target = fingerprint.toCharArray();
byte[] actual = pk.getFingerprint();
for (int i=0; i<20; i++) {
int b1 = hex(target[i*2]);
int b2 = hex(target[i*2+1]);
int cmp = (actual[i] & 0xff);
if ((b1 < 0) || (b2 < 0)) { return false; }
if (cmp != (b1*16 + b2)) {
return false;
}
}
return true;
}
public final static String armor(byte data[])
throws IOException
{
ByteArrayOutputStream baout = new ByteArrayOutputStream();
ArmoredOutputStream aout = new ArmoredOutputStream(baout);
aout.setHeader("Version", "pplv1");
aout.write(data);
aout.close();
return new String(baout.toByteArray(), "utf-8");
}
public final static String toHex(byte[] data)
{
StringBuilder sb = new StringBuilder();
for (int i=0; i<data.length; i++) {
int v = ((int)data[i]) & 0xff;
if (v < 0x10) {
sb.append("0");
}
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
public final static String asFingerprint(PGPPublicKeyRing pkr)
{ return asFingerprint(pkr.getPublicKey()); }
public final static String asFingerprint(PGPPublicKey pk)
{ return toHex(pk.getFingerprint()); }
@SuppressWarnings("unchecked")
public final static PGPSecretKeyRing updateWithNewPassword
(PGPSecretKeyRing skr,
char[] old_password, char[] new_password, int s2kcount)
throws PGPException
{
Iterator<PGPSecretKey> okeyi = skr.getSecretKeys();
ArrayList<PGPSecretKey> tochange = new ArrayList<PGPSecretKey>();
while (okeyi.hasNext()) {
PGPSecretKey osk = okeyi.next();
if (osk.getKeyEncryptionAlgorithm() !=
SymmetricKeyAlgorithmTags.AES_128) {
throw new PGPException
("Only read AES-128 encrypted secret keys");
}
tochange.add(osk);
}
for (PGPSecretKey osk: tochange) {
PGPSecretKey nsk =
PGPSecretKey.copyWithNewPassword
(osk, new BcPBESecretKeyDecryptorBuilder
(new BcPGPDigestCalculatorProvider()).build(old_password),
new BcPBESecretKeyEncryptorBuilder
(PGPEncryptedData.AES_128,
new BcPGPDigestCalculatorProvider()
.get(HashAlgorithmTags.SHA256),
s2kcount).build(new_password));
skr = PGPSecretKeyRing.insertSecretKey(skr, nsk);
}
return skr;
}
public final static PGPPrivateKey extractPrivateKey
(PGPSecretKey sk, char[] password)
throws PGPException
{
if (sk.getKeyEncryptionAlgorithm() !=
SymmetricKeyAlgorithmTags.AES_128) {
throw new PGPException
("Only read AES-128 encrypted secret keys");
}
return sk.extractPrivateKey
(new BcPBESecretKeyDecryptorBuilder
(new BcPGPDigestCalculatorProvider()).build(password));
}
@SuppressWarnings("unchecked")
public final static PGPSecretKeyRing readSecretKeyRing(InputStream inp)
throws PGPException, IOException, SignatureException
{
PGPSecretKeyRing skr = CStrictKeyParser.parseSecretKeyRing(inp);
// Similar steps as public keys. Verify we have exactly one
// master key, and that it has a good self-signature.
PGPSecretKey master = null;
for (Iterator<PGPSecretKey> ski = skr.getSecretKeys();
ski.hasNext();) {
PGPSecretKey cur = ski.next();
if (cur.isMasterKey()) {
if (master == null) {
checkMasterKey(cur.getPublicKey(), null);
master = cur;
}
else {
throw new PGPException("multiple master keys");
}
}
}
if (master == null) {
throw new PGPException("no master key found");
}
// Every subkey must be signed by the master.
for (Iterator<PGPSecretKey> ski = skr.getSecretKeys();
ski.hasNext();) {
PGPSecretKey cur = ski.next();
if (!cur.isMasterKey()) {
checkSubKey(cur.getPublicKey(), master.getPublicKey());
}
}
return skr;
}
public final static PGPKeyRingGenerator generateKeyRingGenerator
(String uid, char[] pass, int s2kcount)
throws PGPException
{
// This object generates individual key-pairs.
RSAKeyPairGenerator kpg = new RSAKeyPairGenerator();
// Boilerplate RSA parameters, no need to change anything
// except for the RSA key-size (2048). You can use whatever
// key-size makes sense for you -- 4096, etc.
kpg.init
(new RSAKeyGenerationParameters
(BigInteger.valueOf(0x10001),
new SecureRandom(), 2048, 12));
// First create the master (signing) key with the generator.
PGPKeyPair rsakp_sign =
new BcPGPKeyPair
(PGPPublicKey.RSA_SIGN, kpg.generateKeyPair(), new Date());
// Then an encryption subkey.
PGPKeyPair rsakp_enc =
new BcPGPKeyPair
(PGPPublicKey.RSA_ENCRYPT, kpg.generateKeyPair(), new Date());
// Add a self-signature on the id
PGPSignatureSubpacketGenerator signhashgen =
new PGPSignatureSubpacketGenerator();
// Add signed metadata on the signature.
// 1) Declare its purpose
signhashgen.setKeyFlags
(false, KeyFlags.SIGN_DATA|KeyFlags.CERTIFY_OTHER);
// 2) Set preferences for secondary crypto algorithms to use
// when sending messages to this key.
signhashgen.setPreferredSymmetricAlgorithms
(false, new int[] {
SymmetricKeyAlgorithmTags.AES_128
});
signhashgen.setPreferredHashAlgorithms
(false, new int[] {
HashAlgorithmTags.SHA256
});
// 3) Request senders add additional checksums to the
// message (useful when verifying unsigned messages.)
signhashgen.setFeature
(false, Features.FEATURE_MODIFICATION_DETECTION);
// Create a signature on the encryption subkey.
PGPSignatureSubpacketGenerator enchashgen =
new PGPSignatureSubpacketGenerator();
// Add metadata to declare its purpose
enchashgen.setKeyFlags
(false, KeyFlags.ENCRYPT_COMMS|KeyFlags.ENCRYPT_STORAGE);
// Objects used to encrypt the secret key.
PGPDigestCalculator sha1Calc =
new BcPGPDigestCalculatorProvider()
.get(HashAlgorithmTags.SHA1);
PGPDigestCalculator sha256Calc =
new BcPGPDigestCalculatorProvider()
.get(HashAlgorithmTags.SHA256);
// bcpg 1.48 exposes this API that includes s2kcount. Earlier
// versions use a default of 0x60.
PBESecretKeyEncryptor pske =
(new BcPBESecretKeyEncryptorBuilder
(PGPEncryptedData.AES_128, sha256Calc, s2kcount))
.build(pass);
// Finally, create the keyring itself. The constructor
// takes parameters that allow it to generate the self
// signature.
PGPKeyRingGenerator keyRingGen =
new PGPKeyRingGenerator
(PGPSignature.POSITIVE_CERTIFICATION, rsakp_sign,
/*CStrictKeyParser.PPL_MAGIC*/uid,
sha1Calc, signhashgen.generate(), null,
new BcPGPContentSignerBuilder
(rsakp_sign.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA1),
pske);
// Add our encryption subkey, together with its signature.
keyRingGen.addSubKey
(rsakp_enc, enchashgen.generate(), null);
return keyRingGen;
}
@SuppressWarnings("unchecked")
public final static PGPSecretKey getEncryptionKey(PGPSecretKeyRing skr)
throws PGPException
{
Iterator<PGPSecretKey> skit = skr.getSecretKeys();
while (skit.hasNext()) {
PGPSecretKey cur = skit.next();
if (cur.getPublicKey().isEncryptionKey()) {
return cur;
}
}
throw new PGPException("No encryption key found");
}
@SuppressWarnings("unchecked")
public final static PGPPublicKey getEncryptionKey(PGPPublicKeyRing pkr)
throws PGPException
{
Iterator<PGPPublicKey> pkit = pkr.getPublicKeys();
while (pkit.hasNext()) {
PGPPublicKey cur = pkit.next();
if (cur.isEncryptionKey()) {
return cur;
}
}
throw new PGPException("No encryption key found");
}
@SuppressWarnings("unchecked")
public final static PGPSecretKey getSigningKey(PGPSecretKeyRing skr)
throws PGPException
{
Iterator<PGPSecretKey> skit = skr.getSecretKeys();
while (skit.hasNext()) {
PGPSecretKey cur = skit.next();
if (cur.isSigningKey()) {
return cur;
}
}
throw new PGPException("No signing key found");
}
// Simply check that this is a reasonably valid encrypted file.
public final static void checkEncrypted(InputStream in)
throws PGPException, IOException
{
// Expect a list of session keys, followed by a single
// encrypted packet.
PGPObjectFactory pgpF = new PGPObjectFactory(in);
Object o = pgpF.nextObject();
if (!(o instanceof PGPEncryptedDataList)) {
throw new PGPException("Missing encrypted session keys.");
}
PGPEncryptedDataList edl = (PGPEncryptedDataList) o;
// Ensure that we only accept anonymous public key packets
int count = edl.size();
if (count == 0) {
throw new PGPException("Missing encrypted session keys.");
}
PGPPublicKeyEncryptedData any = null;
for (int i=0; i<count; i++) {
o = edl.get(i);
if (!(o instanceof PGPPublicKeyEncryptedData)) {
throw new PGPException("Only accept public key encrypted data");
}
any = ((PGPPublicKeyEncryptedData)o);
if (any.getKeyID() != 0l) {
throw new PGPException("Only accept anonymous encrypted data");
}
if (!any.isIntegrityProtected()) {
throw new PGPException("Only accept integrity protected data");
}
}
}
public final static void extractEncryptedData
(InputStream in, PGPPublicKey signkey, PGPPrivateKey enckey,
OutputStream out)
throws PGPException, IOException, SignatureException
{
// Expect a list of session keys, followed by a single
// encrypted packet.
PGPObjectFactory pgpF = new PGPObjectFactory(in);
Object o = pgpF.nextObject();
if (!(o instanceof PGPEncryptedDataList)) {
throw new PGPException("Missing encrypted session keys.");
}
PGPEncryptedDataList edl = (PGPEncryptedDataList) o;
// Ensure that we only accept anonymous public key packets,
// and pull out our data stream from one of the session keys.
PublicKeyDataDecryptorFactory df =
new BcPublicKeyDataDecryptorFactory(enckey);
int count = edl.size();
InputStream dataStream = null;
PGPPublicKeyEncryptedData edata = null;
for (int i=0; i<count; i++) {
o = edl.get(i);
if (!(o instanceof PGPPublicKeyEncryptedData)) {
throw new PGPException("Only accept public key encrypted data");
}
PGPPublicKeyEncryptedData cur = ((PGPPublicKeyEncryptedData)o);
if (cur.getKeyID() != 0l) {
throw new PGPException("Only accept anonymous encrypted data");
}
// If we haven't seen it already, check if we can get the
// stream.
if (dataStream == null) {
try {
dataStream = cur.getDataStream(df);
edata = cur;
}
catch (Throwable ign) {
// Not ours -- keep going.
dataStream = null;
edata = null;
}
}
}
if (dataStream == null) {
throw new PGPException("Cannot decrypt data");
}
// Confirm that we're not seeing unencrypted traffic.
if (edata.getSymmetricAlgorithm(df) ==
SymmetricKeyAlgorithmTags.NULL) {
throw new PGPException("Won't accept unencrypted data");
}
if (!edata.isIntegrityProtected()) {
throw new PGPException
("Only accept integrity protected data");
}
extractSignedData(dataStream, signkey, out);
if (!edata.verify()) {
throw new PGPException("Integrity check failed");
}
}
public final static void extractSignedData
(InputStream in, PGPPublicKey signkey, OutputStream out)
throws PGPException, IOException, SignatureException
{
// Use convenience methods from PGPObjectFactory
PGPObjectFactory objectStream = new PGPObjectFactory(in);
// allow a single compressed packet if present.
Object o = objectStream.nextObject();
if (o instanceof PGPCompressedData) {
// use a nested objectStream instead.
objectStream =
new PGPObjectFactory(((PGPCompressedData) o).getDataStream());
o = objectStream.nextObject();
}
// We expect to see a list of signature headers, then a
// literal packet, and another list of signatures.
// 1. One-pass headers.
if (!(o instanceof PGPOnePassSignatureList)) {
throw new PGPException("Missing signature header: "+o);
}
// Pull out a signature that we're willing to use.
PGPOnePassSignature mysignature =
signatureFor((PGPOnePassSignatureList) o, signkey);
// 2. Literal packet.
o = objectStream.nextObject();
if (!(o instanceof PGPLiteralData)) {
throw new PGPException("Missing literal data");
}
PGPLiteralData ldata = (PGPLiteralData) o;
// 2.5 Copy the data to the outputstream, and also
// update the signature so we may verify it.
mysignature.init(s_provider, signkey);
InputStream ldin = ldata.getInputStream();
byte buf[] = new byte[8192];
int nread;
while ((nread = ldin.read(buf)) > 0) {
out.write(buf, 0, nread);
mysignature.update(buf, 0, nread);
}
out.close();
// 3. Signature trailer.
o = objectStream.nextObject();
if (!(o instanceof PGPSignatureList)) {
throw new PGPException("Missing signature");
}
if (!mysignature.verify
(signatureFor((PGPSignatureList)o, signkey))) {
throw new PGPException("bad signature, reject data");
}
if (objectStream.nextObject() != null) {
throw new PGPException("Unexpected trailing data, reject");
}
}
private final static PGPSignature signatureFor
(PGPSignatureList siglist, PGPPublicKey pk)
throws PGPException
{
int count = siglist.size();
for (int i=0; i<count; i++) {
PGPSignature cur = siglist.get(i);
// Approximate match -- "close enough" for typical
// needs.
if (cur.getKeyID() == pk.getKeyID()) {
return cur;
}
}
throw new PGPException("No usable signatures.");
}
private final static PGPOnePassSignature signatureFor
(PGPOnePassSignatureList siglist, PGPPublicKey pk)
throws PGPException
{
int count = siglist.size();
for (int i=0; i<count; i++) {
PGPOnePassSignature cur = siglist.get(i);
// Approximate match -- "close enough" for typical
// needs.
if (cur.getKeyID() == pk.getKeyID()) {
return cur;
}
}
throw new PGPException("No usable signatures.");
}
public final static void writeEncryptedSignedData
(File src, OutputStream out,
List<PGPPublicKey> enckeys, PGPPrivateKey signkey,
String name, Date timestamp, boolean compress)
throws IOException, PGPException, SignatureException
{
PGPEncryptedDataGenerator encGen =
new PGPEncryptedDataGenerator
(new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_128)
.setWithIntegrityPacket(true));
for (PGPPublicKey enckey: enckeys) {
encGen.addMethod
(new CAnonymousPublicKeyKeyEncryptionMethodGenerator(enckey));
}
OutputStream encOut = encGen.open(out, new byte[1<<16]);
writeSignedData(src, encOut, signkey, name, timestamp, compress);
encGen.close();
}
public final static void writeSignedData
(File src, OutputStream out, PGPPrivateKey signkey,
String name, Date timestamp, boolean compress)
throws PGPException, IOException, SignatureException
{
OutputStream comOut;
PGPCompressedDataGenerator comGen;
if (compress) {
// Wrap everything in a compressed packet.
comGen = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
comOut = comGen.open(out);
}
else {
comGen = null;
comOut = out;
}
// Dump 3 packets.
// one-pass signature header
// literal data
// signature
PGPSignatureGenerator sGen =
new PGPSignatureGenerator
(new BcPGPContentSignerBuilder
(signkey.getPublicKeyPacket().getAlgorithm(),
HashAlgorithmTags.SHA256));
sGen.init(PGPSignature.BINARY_DOCUMENT, signkey);
// 1. Header packet
sGen.generateOnePassVersion(false).encode(comOut);
// 2. Literal data packet.
writeLiteralPacket(src, comOut, sGen, name, timestamp);
// 3. Write signature
sGen.generate().encode(comOut);
if (comGen != null) {
// Close compressor to flush out last bits.
comGen.close();
}
}
// Add an "i-trust-this-user-id" signature to the master key,
// signed by the provided private key. (Used to approve other
// people's public keys once verified.)
//
public final static PGPPublicKeyRing certifyPublicKeyRing
(PGPPublicKeyRing certifypkr, PGPPrivateKey sign_key)
throws PGPException, SignatureException
{
PGPPublicKey orig_master = certifypkr.getPublicKey();
if (!orig_master.isMasterKey()) {
throw new IllegalArgumentException("Unexpected - not master");
}
String uid = getExactlyOneUserId(orig_master);
// Create a signature for it.
PGPSignatureGenerator sgen =
new PGPSignatureGenerator
(new BcPGPContentSignerBuilder
(sign_key.getPublicKeyPacket().getAlgorithm(),
HashAlgorithmTags.SHA256));
sgen.init(PGPSignature.DEFAULT_CERTIFICATION, sign_key);
PGPPublicKey certified_master = PGPPublicKey.addCertification
(orig_master, uid, sgen.generateCertification(uid, orig_master));
return PGPPublicKeyRing.insertPublicKey(certifypkr, certified_master);
}
@SuppressWarnings("unchecked")
private final static String getExactlyOneUserId(PGPPublicKey pk)
throws PGPException
{
Iterator<String> uids = pk.getUserIDs();
if ((uids == null) || (!uids.hasNext())) {
throw new PGPException("No userids available");
}
String ret = uids.next();
if (uids.hasNext()) {
throw new PGPException("Multiple userids found");
}
return ret;
}
private final static int hex(char cur)
{
if ((cur >= '0') && (cur <= '9')) {
return (cur - '0');
}
if ((cur >= 'a') & (cur <= 'f')) {
return (cur - 'a') + 10;
}
if ((cur >= 'A') && (cur <= 'F')) {
return (cur - 'A') + 10;
}
return -1;
}
private final static void writeLiteralPacket
(File src, OutputStream out, PGPSignatureGenerator sGen,
String name, Date timestamp)
throws IOException, SignatureException
{
PGPLiteralDataGenerator ldGen = new PGPLiteralDataGenerator();
FileInputStream fin = null;
try {
fin = new FileInputStream(src);
OutputStream ldOut = ldGen.open
(out, PGPLiteralData.BINARY, name, fin.available(), timestamp);
byte[] buf = new byte[4192];
int nread;
while ((nread = fin.read(buf)) > 0) {
ldOut.write(buf, 0, nread);
sGen.update(buf, 0, nread);
}
ldGen.close();
}
finally {
if (fin != null) {
try { fin.close(); } catch (Throwable ign) {}
}
}
}
@SuppressWarnings("unchecked")
private final static void checkSubKey
(PGPPublicKey subkey, PGPPublicKey master)
throws PGPException,SignatureException
{
Iterator<PGPSignature> sigs = subkey.getSignatures();
boolean ok = false;
if (sigs == null) {
throw new PGPException("No signatures available");
}
// See if any of the subkey-certifying signatures from
// the subkey work.
while (sigs.hasNext()) {
PGPSignature sig = sigs.next();
if ((sig.getKeyID() != master.getKeyID()) ||
(sig.getSignatureType() != PGPSignature.SUBKEY_BINDING)) {
continue;
}
sig.init(s_provider, master);
ok = sig.verifyCertification(master, subkey);
if (ok) {
break;
}
}
if (!ok) {
throw new PGPException("Subkey not signed by master key");
}
}
@SuppressWarnings("unchecked")
private final static void checkMasterKey
(PGPPublicKey master, PGPPublicKey certifying_key)
throws PGPException,SignatureException
{
// 1) The key should not have expired.
long vs = master.getValidSeconds();
if (vs != 0) {
long expire = master.getCreationTime().getTime()+(vs*1000);
long now = System.currentTimeMillis();
if (expire < now) {
throw new PGPException
("Sorry, this certificate expired on "+
new Date(expire));
}
}
// 2) Collect all the user-ids, and ensure that we
// have a good self-signature and if needed, a
// certifying signature for each one.
Map<String,Boolean> checked_uids = new HashMap<String,Boolean>();
Map<String,Boolean> certified_uids;
if (certifying_key != null) {
certified_uids = new HashMap<String,Boolean>();
}
else {
certified_uids = null;
}
for (Iterator<String> uids = master.getUserIDs(); uids.hasNext();) {
String uid = uids.next();
checked_uids.put(uid, false);
if (certified_uids != null) {
certified_uids.put(uid, false);
}
}
if (checked_uids.isEmpty()) {
throw new PGPException("No uids found for master key.");
}
Iterator<PGPSignature> sigs = master.getSignatures();
if (sigs == null) {
throw new PGPException("No signatures available on master key.");
}
while (sigs.hasNext()) {
PGPSignature sig = sigs.next();
// Ignore anything that's not a self-certification.
if ((sig.getSignatureType() < PGPSignature.DEFAULT_CERTIFICATION) ||
(sig.getSignatureType() > PGPSignature.POSITIVE_CERTIFICATION)){
continue;
}
// Proceed only if the signature is signed with either the
// master key, or the certifying_key (if non-null)
if (sig.getKeyID() != master.getKeyID()) {
if ((certifying_key == null) ||
(sig.getKeyID() != certifying_key.getKeyID())) {
continue;
}
}
// See if it can certify any unverified uid
if (sig.getKeyID() == master.getKeyID()) {
updateVerification(checked_uids, sig, master, master);
}
// Hits this only if certifying_key != null. If I screw up,
// NPE -- is also fine.
else if (sig.getKeyID() == certifying_key.getKeyID()) {
updateVerification(certified_uids, sig, certifying_key, master);
}
else {
throw new IllegalStateException("Should not be here!");
}
}
// 3) Make sure every uid in the key has a good signature.
for (String uid: checked_uids.keySet()) {
if (!checked_uids.get(uid)) {
throw new PGPException("No signature found for "+uid);
}
}
if (certified_uids != null) {
for (String uid: certified_uids.keySet()) {
if (!certified_uids.get(uid)) {
throw new PGPException
("No certification found for "+uid);
}
}
}
}
private final static void updateVerification
(Map<String,Boolean> checked, PGPSignature sig,
PGPPublicKey signer, PGPPublicKey tobecertified)
throws PGPException, SignatureException
{
for (String uid: checked.keySet()) {
if (checked.get(uid)) {
continue;
}
sig.init(s_provider, signer);
if (sig.verifyCertification(uid, tobecertified)) {
checked.put(uid, true);
break;
}
}
}
private final static BcPGPContentVerifierBuilderProvider s_provider =
new BcPGPContentVerifierBuilderProvider();
private static final BigInteger ONE = BigInteger.valueOf(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment