List of usage examples for org.bouncycastle.openpgp PGPPublicKey getUserAttributes
public Iterator<PGPUserAttributeSubpacketVector> getUserAttributes()
From source file:org.kontalk.xmppserver.pgp.PGPUtils.java
License:Open Source License
public static PGPPublicKeyRing merge(PGPPublicKeyRing oldRing, PGPPublicKeyRing newRing) throws PGPException, IOException { if (!equals(oldRing, newRing)) { throw new PGPKeyValidationException("keys are not equal"); }// w ww .j av a 2s . c o m // remember which certs we already added. this is cheaper than semantic deduplication Set<byte[]> certs = new TreeSet<>(new Comparator<byte[]>() { public int compare(byte[] left, byte[] right) { // check for length equality if (left.length != right.length) { return left.length - right.length; } // compare byte-by-byte for (int i = 0; i < left.length; i++) { if (left[i] != right[i]) { return (left[i] & 0xff) - (right[i] & 0xff); } } // ok they're the same return 0; } }); PGPPublicKeyRing result = oldRing; PGPPublicKeyRing candidate = newRing; // Pre-load all existing certificates for (@SuppressWarnings("unchecked") Iterator<PGPPublicKey> i = result.getPublicKeys(); i.hasNext();) { PGPPublicKey key = i.next(); for (@SuppressWarnings("unchecked") Iterator<PGPSignature> j = key.getSignatures(); j.hasNext();) { PGPSignature cert = j.next(); certs.add(cert.getEncoded()); } } for (@SuppressWarnings("unchecked") Iterator<PGPPublicKey> i = candidate.getPublicKeys(); i.hasNext();) { PGPPublicKey key = i.next(); final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID()); if (resultKey == null) { // otherwise, just insert the public key result = PGPPublicKeyRing.insertPublicKey(result, key); continue; } // Modifiable version of the old key, which we merge stuff into (keep old for comparison) PGPPublicKey modified = resultKey; // Iterate certifications for (@SuppressWarnings("unchecked") Iterator<PGPSignature> j = key.getSignatures(); j.hasNext();) { PGPSignature cert = j.next(); byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { continue; } certs.add(encoded); modified = PGPPublicKey.addCertification(modified, cert); } // If this is a subkey, merge it in and stop here if (!key.isMasterKey()) { if (modified != resultKey) { result = PGPPublicKeyRing.insertPublicKey(result, modified); } continue; } // Copy over all user id certificates for (@SuppressWarnings("unchecked") Iterator<byte[]> r = key.getRawUserIDs(); r.hasNext();) { byte[] rawUserId = r.next(); @SuppressWarnings("unchecked") Iterator<PGPSignature> signaturesIt = key.getSignaturesForID(rawUserId); // no signatures for this User ID, skip it if (signaturesIt == null) { continue; } while (signaturesIt.hasNext()) { PGPSignature cert = signaturesIt.next(); byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { continue; } certs.add(encoded); modified = PGPPublicKey.addCertification(modified, rawUserId, cert); } } // Copy over all user attribute certificates for (@SuppressWarnings("unchecked") Iterator<PGPUserAttributeSubpacketVector> v = key.getUserAttributes(); v.hasNext();) { PGPUserAttributeSubpacketVector vector = v.next(); @SuppressWarnings("unchecked") Iterator<PGPSignature> signaturesIt = key.getSignaturesForUserAttribute(vector); // no signatures for this user attribute attribute, skip it if (signaturesIt == null) { continue; } while (signaturesIt.hasNext()) { PGPSignature cert = signaturesIt.next(); byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { continue; } certs.add(encoded); modified = PGPPublicKey.addCertification(modified, vector, cert); } } // If anything change, save the updated (sub)key if (modified != resultKey) { result = PGPPublicKeyRing.insertPublicKey(result, modified); } } return result; }
From source file:org.sufficientlysecure.keychain.pgp.UncachedKeyRing.java
License:Open Source License
/** "Canonicalizes" a public key, removing inconsistencies in the process. * * More specifically:/* w w w .j av a 2 s .c om*/ * - Remove all non-verifying self-certificates * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" * - For UID certificates, remove all certificates which are * superseded by a newer one on the same target, including * revocations with later re-certifications. * - For subkey certifications, remove all certificates which * are superseded by a newer one on the same target, unless * it encounters a revocation certificate. The revocation * certificate is considered to permanently revoke the key, * even if contains later re-certifications. * This is the "behavior in practice" used by (e.g.) GnuPG, and * the rationale for both can be found as comments in the GnuPG * source. * UID signatures: * https://github.com/mtigas/gnupg/blob/50c98c7ed6b542857ee2f902eca36cda37407737/g10/getkey.c#L1668-L1674 * Subkey signatures: * https://github.com/mtigas/gnupg/blob/50c98c7ed6b542857ee2f902eca36cda37407737/g10/getkey.c#L1990-L1997 * - Remove all certificates in other positions if not of known type: * - key revocation signatures on the master key * - subkey binding signatures for subkeys * - certifications and certification revocations for user ids * - If a subkey retains no valid subkey binding certificate, remove it * - If a user id retains no valid self certificate, remove it * - If the key is a secret key, remove all certificates by foreign keys * - If no valid user id remains, log an error and return null * * This operation writes an OperationLog which can be used as part of an OperationResultParcel. * * @param forExport if this is true, non-exportable signatures will be removed * @return A canonicalized key, or null on fatal error (log will include a message in this case) * */ @SuppressWarnings("ConstantConditions") public CanonicalizedKeyRing canonicalize(OperationLog log, int indent, boolean forExport) { log.add(isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC, indent, KeyFormattingUtils.convertKeyIdToHex(getMasterKeyId())); indent += 1; // do not accept v3 keys if (getVersion() <= 3) { log.add(LogType.MSG_KC_ERROR_V3, indent); return null; } Calendar nowCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); // allow for diverging clocks up to one day when checking creation time nowCal.add(Calendar.DAY_OF_YEAR, 1); final Date nowPlusOneDay = nowCal.getTime(); int redundantCerts = 0, badCerts = 0; PGPKeyRing ring = mRing; PGPPublicKey masterKey = mRing.getPublicKey(); final long masterKeyId = masterKey.getKeyID(); if (Arrays.binarySearch(KNOWN_ALGORITHMS, masterKey.getAlgorithm()) < 0) { log.add(LogType.MSG_KC_ERROR_MASTER_ALGO, indent, Integer.toString(masterKey.getAlgorithm())); return null; } { log.add(LogType.MSG_KC_MASTER, indent, KeyFormattingUtils.convertKeyIdToHex(masterKey.getKeyID())); indent += 1; PGPPublicKey modified = masterKey; PGPSignature revocation = null; PGPSignature notation = null; for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) { int type = zert.getSignatureType(); // These should most definitely not be here... if (type == PGPSignature.NO_CERTIFICATION || type == PGPSignature.DEFAULT_CERTIFICATION || type == PGPSignature.CASUAL_CERTIFICATION || type == PGPSignature.POSITIVE_CERTIFICATION || type == PGPSignature.CERTIFICATION_REVOCATION) { log.add(LogType.MSG_KC_MASTER_BAD_TYPE_UID, indent); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } WrappedSignature cert = new WrappedSignature(zert); if (type != PGPSignature.KEY_REVOCATION && type != PGPSignature.DIRECT_KEY) { // Unknown type, just remove log.add(LogType.MSG_KC_MASTER_BAD_TYPE, indent, "0x" + Integer.toString(type, 16)); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } if (cert.getCreationTime().after(nowPlusOneDay)) { // Creation date in the future? No way! log.add(LogType.MSG_KC_MASTER_BAD_TIME, indent); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } try { cert.init(masterKey); if (!cert.verifySignature(masterKey)) { log.add(LogType.MSG_KC_MASTER_BAD, indent); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogType.MSG_KC_MASTER_BAD_ERR, indent); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } // if this is for export, we always remove any non-exportable certs if (forExport && cert.isLocal()) { // Remove revocation certs with "local" flag log.add(LogType.MSG_KC_MASTER_LOCAL, indent); modified = PGPPublicKey.removeCertification(modified, zert); continue; } // special case: non-exportable, direct key signatures for notations! if (cert.getSignatureType() == PGPSignature.DIRECT_KEY) { // must be local, otherwise strip! if (!cert.isLocal()) { log.add(LogType.MSG_KC_MASTER_BAD_TYPE, indent); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } // first notation? fine then. if (notation == null) { notation = zert; // more notations? at least one is superfluous, then. } else if (notation.getCreationTime().before(zert.getCreationTime())) { log.add(LogType.MSG_KC_NOTATION_DUP, indent); modified = PGPPublicKey.removeCertification(modified, notation); redundantCerts += 1; notation = zert; } else { log.add(LogType.MSG_KC_NOTATION_DUP, indent); modified = PGPPublicKey.removeCertification(modified, zert); redundantCerts += 1; } continue; } else if (cert.isLocal()) { // Remove revocation certs with "local" flag log.add(LogType.MSG_KC_MASTER_BAD_LOCAL, indent); modified = PGPPublicKey.removeCertification(modified, zert); badCerts += 1; continue; } // first revocation? fine then. if (revocation == null) { revocation = zert; // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(zert.getCreationTime())) { log.add(LogType.MSG_KC_REVOKE_DUP, indent); modified = PGPPublicKey.removeCertification(modified, revocation); redundantCerts += 1; revocation = zert; } else { log.add(LogType.MSG_KC_REVOKE_DUP, indent); modified = PGPPublicKey.removeCertification(modified, zert); redundantCerts += 1; } } // If we have a notation packet, check if there is even any data in it? if (notation != null) { // If there isn't, might as well strip it if (new WrappedSignature(notation).getNotation().isEmpty()) { log.add(LogType.MSG_KC_NOTATION_EMPTY, indent); modified = PGPPublicKey.removeCertification(modified, notation); redundantCerts += 1; } } ArrayList<String> processedUserIds = new ArrayList<>(); for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) { String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId); // warn if user id was made with bad encoding if (!Utf8Util.isValidUTF8(rawUserId)) { log.add(LogType.MSG_KC_UID_WARN_ENCODING, indent); } // check for duplicate user ids if (processedUserIds.contains(userId)) { log.add(LogType.MSG_KC_UID_DUP, indent, userId); // strip out the first found user id with this name modified = PGPPublicKey.removeCertification(modified, rawUserId); } if (processedUserIds.size() > CANONICALIZE_MAX_USER_IDS) { log.add(LogType.MSG_KC_UID_TOO_MANY, indent, userId); // strip out the user id modified = PGPPublicKey.removeCertification(modified, rawUserId); } processedUserIds.add(userId); PGPSignature selfCert = null; revocation = null; // look through signatures for this specific user id @SuppressWarnings("unchecked") Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForID(rawUserId); if (signaturesIt != null) { for (PGPSignature zert : new IterableIterator<>(signaturesIt)) { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); int type = zert.getSignatureType(); if (type != PGPSignature.DEFAULT_CERTIFICATION && type != PGPSignature.NO_CERTIFICATION && type != PGPSignature.CASUAL_CERTIFICATION && type != PGPSignature.POSITIVE_CERTIFICATION && type != PGPSignature.CERTIFICATION_REVOCATION) { log.add(LogType.MSG_KC_UID_BAD_TYPE, indent, "0x" + Integer.toString(zert.getSignatureType(), 16)); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); badCerts += 1; continue; } if (cert.getCreationTime().after(nowPlusOneDay)) { // Creation date in the future? No way! log.add(LogType.MSG_KC_UID_BAD_TIME, indent); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); badCerts += 1; continue; } if (cert.isLocal()) { // Creation date in the future? No way! log.add(LogType.MSG_KC_UID_BAD_LOCAL, indent); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); badCerts += 1; continue; } // If this is a foreign signature, ... if (certId != masterKeyId) { // never mind any further for public keys, but remove them from secret ones if (isSecret()) { log.add(LogType.MSG_KC_UID_FOREIGN, indent, KeyFormattingUtils.convertKeyIdToHex(certId)); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); badCerts += 1; } continue; } // Otherwise, first make sure it checks out try { cert.init(masterKey); if (!cert.verifySignature(masterKey, rawUserId)) { log.add(LogType.MSG_KC_UID_BAD, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogType.MSG_KC_UID_BAD_ERR, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); badCerts += 1; continue; } switch (type) { case PGPSignature.DEFAULT_CERTIFICATION: case PGPSignature.NO_CERTIFICATION: case PGPSignature.CASUAL_CERTIFICATION: case PGPSignature.POSITIVE_CERTIFICATION: if (selfCert == null) { selfCert = zert; } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { log.add(LogType.MSG_KC_UID_CERT_DUP, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, selfCert); redundantCerts += 1; selfCert = zert; } else { log.add(LogType.MSG_KC_UID_CERT_DUP, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); redundantCerts += 1; } // If there is a revocation certificate, and it's older than this, drop it if (revocation != null && revocation.getCreationTime().before(selfCert.getCreationTime())) { log.add(LogType.MSG_KC_UID_REVOKE_OLD, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, revocation); revocation = null; redundantCerts += 1; } break; case PGPSignature.CERTIFICATION_REVOCATION: // If this is older than the (latest) self cert, drop it if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { log.add(LogType.MSG_KC_UID_REVOKE_OLD, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); redundantCerts += 1; continue; } // first revocation? remember it. if (revocation == null) { revocation = zert; // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(cert.getCreationTime())) { log.add(LogType.MSG_KC_UID_REVOKE_DUP, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, revocation); redundantCerts += 1; revocation = zert; } else { log.add(LogType.MSG_KC_UID_REVOKE_DUP, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert); redundantCerts += 1; } break; } } } // If no valid certificate (if only a revocation) remains, drop it if (selfCert == null && revocation == null) { log.add(LogType.MSG_KC_UID_REMOVE, indent, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId); } } // If NO user ids remain, error out! if (modified == null || !modified.getUserIDs().hasNext()) { log.add(LogType.MSG_KC_ERROR_NO_UID, indent); return null; } ArrayList<PGPUserAttributeSubpacketVector> processedUserAttributes = new ArrayList<>(); for (PGPUserAttributeSubpacketVector userAttribute : new IterableIterator<PGPUserAttributeSubpacketVector>( masterKey.getUserAttributes())) { if (userAttribute.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) { log.add(LogType.MSG_KC_UAT_JPEG, indent); } else { log.add(LogType.MSG_KC_UAT_UNKNOWN, indent); } try { indent += 1; // check for duplicate user attributes if (processedUserAttributes.contains(userAttribute)) { log.add(LogType.MSG_KC_UAT_DUP, indent); // strip out the first found user id with this name modified = PGPPublicKey.removeCertification(modified, userAttribute); } processedUserAttributes.add(userAttribute); PGPSignature selfCert = null; revocation = null; // look through signatures for this specific user id @SuppressWarnings("unchecked") Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForUserAttribute(userAttribute); if (signaturesIt != null) { for (PGPSignature zert : new IterableIterator<>(signaturesIt)) { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); int type = zert.getSignatureType(); if (type != PGPSignature.DEFAULT_CERTIFICATION && type != PGPSignature.NO_CERTIFICATION && type != PGPSignature.CASUAL_CERTIFICATION && type != PGPSignature.POSITIVE_CERTIFICATION && type != PGPSignature.CERTIFICATION_REVOCATION) { log.add(LogType.MSG_KC_UAT_BAD_TYPE, indent, "0x" + Integer.toString(zert.getSignatureType(), 16)); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); badCerts += 1; continue; } if (cert.getCreationTime().after(nowPlusOneDay)) { // Creation date in the future? No way! log.add(LogType.MSG_KC_UAT_BAD_TIME, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); badCerts += 1; continue; } if (cert.isLocal()) { // Creation date in the future? No way! log.add(LogType.MSG_KC_UAT_BAD_LOCAL, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); badCerts += 1; continue; } // If this is a foreign signature, ... if (certId != masterKeyId) { // never mind any further for public keys, but remove them from secret ones if (isSecret()) { log.add(LogType.MSG_KC_UAT_FOREIGN, indent, KeyFormattingUtils.convertKeyIdToHex(certId)); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); badCerts += 1; } continue; } // Otherwise, first make sure it checks out try { cert.init(masterKey); if (!cert.verifySignature(masterKey, userAttribute)) { log.add(LogType.MSG_KC_UAT_BAD, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogType.MSG_KC_UAT_BAD_ERR, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); badCerts += 1; continue; } switch (type) { case PGPSignature.DEFAULT_CERTIFICATION: case PGPSignature.NO_CERTIFICATION: case PGPSignature.CASUAL_CERTIFICATION: case PGPSignature.POSITIVE_CERTIFICATION: if (selfCert == null) { selfCert = zert; } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { log.add(LogType.MSG_KC_UAT_CERT_DUP, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, selfCert); redundantCerts += 1; selfCert = zert; } else { log.add(LogType.MSG_KC_UAT_CERT_DUP, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); redundantCerts += 1; } // If there is a revocation certificate, and it's older than this, drop it if (revocation != null && revocation.getCreationTime().before(selfCert.getCreationTime())) { log.add(LogType.MSG_KC_UAT_REVOKE_OLD, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation); revocation = null; redundantCerts += 1; } break; case PGPSignature.CERTIFICATION_REVOCATION: // If this is older than the (latest) self cert, drop it if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { log.add(LogType.MSG_KC_UAT_REVOKE_OLD, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); redundantCerts += 1; continue; } // first revocation? remember it. if (revocation == null) { revocation = zert; // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(cert.getCreationTime())) { log.add(LogType.MSG_KC_UAT_REVOKE_DUP, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation); redundantCerts += 1; revocation = zert; } else { log.add(LogType.MSG_KC_UAT_REVOKE_DUP, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); redundantCerts += 1; } break; } } } // If no valid certificate (if only a revocation) remains, drop it if (selfCert == null && revocation == null) { log.add(LogType.MSG_KC_UAT_REMOVE, indent); modified = PGPPublicKey.removeCertification(modified, userAttribute); } } finally { indent -= 1; } } // Replace modified key in the keyring ring = replacePublicKey(ring, modified); indent -= 1; } // Keep track of ids we encountered so far Set<Long> knownIds = new HashSet<>(); // Process all keys for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(ring.getPublicKeys())) { // Make sure this is not a duplicate, avoid undefined behavior! if (knownIds.contains(key.getKeyID())) { log.add(LogType.MSG_KC_ERROR_DUP_KEY, indent, KeyFormattingUtils.convertKeyIdToHex(key.getKeyID())); return null; } // Add the key id to known knownIds.add(key.getKeyID()); // Don't care about the master key any further, that one gets special treatment above if (key.isMasterKey()) { continue; } log.add(LogType.MSG_KC_SUB, indent, KeyFormattingUtils.convertKeyIdToHex(key.getKeyID())); indent += 1; if (Arrays.binarySearch(KNOWN_ALGORITHMS, key.getAlgorithm()) < 0) { ring = removeSubKey(ring, key); log.add(LogType.MSG_KC_SUB_UNKNOWN_ALGO, indent, Integer.toString(key.getAlgorithm())); indent -= 1; continue; } Date keyCreationTime = key.getCreationTime(), keyCreationTimeLenient; { Calendar keyCreationCal = Calendar.getInstance(); keyCreationCal.setTime(keyCreationTime); // allow for diverging clocks up to one day when checking creation time keyCreationCal.add(Calendar.MINUTE, -5); keyCreationTimeLenient = keyCreationCal.getTime(); } // A subkey needs exactly one subkey binding certificate, and optionally one revocation // certificate. PGPPublicKey modified = key; PGPSignature selfCert = null, revocation = null; uids: for (PGPSignature zert : new IterableIterator<PGPSignature>(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zert); WrappedSignature cert = new WrappedSignature(zert); int type = cert.getSignatureType(); // filter out bad key types... if (cert.getKeyId() != masterKey.getKeyID()) { log.add(LogType.MSG_KC_SUB_BAD_KEYID, indent); badCerts += 1; continue; } if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { log.add(LogType.MSG_KC_SUB_BAD_TYPE, indent, "0x" + Integer.toString(type, 16)); badCerts += 1; continue; } if (cert.getCreationTime().after(nowPlusOneDay)) { // Creation date in the future? No way! log.add(LogType.MSG_KC_SUB_BAD_TIME, indent); badCerts += 1; continue; } if (cert.getCreationTime().before(keyCreationTime)) { // Signature is earlier than key creation time log.add(LogType.MSG_KC_SUB_BAD_TIME_EARLY, indent); // due to an earlier accident, we generated keys which had creation timestamps // a few seconds after their signature timestamp. for compatibility, we only // error out with some margin of error if (cert.getCreationTime().before(keyCreationTimeLenient)) { badCerts += 1; continue; } } if (cert.isLocal()) { // Creation date in the future? No way! log.add(LogType.MSG_KC_SUB_BAD_LOCAL, indent); badCerts += 1; continue; } if (type == PGPSignature.SUBKEY_BINDING) { // make sure the certificate checks out try { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogType.MSG_KC_SUB_BAD, indent); badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogType.MSG_KC_SUB_BAD_ERR, indent); badCerts += 1; continue; } boolean needsPrimaryBinding = false; // If the algorithm is even suitable for signing if (isSigningAlgo(key.getAlgorithm())) { // If this certificate says it allows signing for the key if (zert.getHashedSubPackets() != null && zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { int flags = ((KeyFlags) zert.getHashedSubPackets() .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); if ((flags & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA) { needsPrimaryBinding = true; } } else { // If there are no key flags, we STILL require this because the key can sign! needsPrimaryBinding = true; } } // If this key can sign, it MUST have a primary key binding certificate if (needsPrimaryBinding) { boolean ok = false; if (zert.getUnhashedSubPackets() != null) try { // Check all embedded signatures, if any of them fits PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures(); for (int i = 0; i < list.size(); i++) { WrappedSignature subsig = new WrappedSignature(list.get(i)); if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { subsig.init(key); if (subsig.verifySignature(masterKey, key)) { ok = true; } else { log.add(LogType.MSG_KC_SUB_PRIMARY_BAD, indent); badCerts += 1; continue uids; } } } } catch (Exception e) { log.add(LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, indent); badCerts += 1; continue; } // if it doesn't, get rid of this! if (!ok) { log.add(LogType.MSG_KC_SUB_PRIMARY_NONE, indent); badCerts += 1; continue; } } // if we already have a cert, and this one is older: skip it if (selfCert != null && cert.getCreationTime().before(selfCert.getCreationTime())) { log.add(LogType.MSG_KC_SUB_DUP, indent); redundantCerts += 1; continue; } selfCert = zert; // it must be a revocation, then (we made sure above) } else { // make sure the certificate checks out try { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogType.MSG_KC_SUB_REVOKE_BAD, indent); badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogType.MSG_KC_SUB_REVOKE_BAD_ERR, indent); badCerts += 1; continue; } // If we already have a newer revocation cert, skip this one. if (revocation != null && revocation.getCreationTime().after(cert.getCreationTime())) { log.add(LogType.MSG_KC_SUB_REVOKE_DUP, indent); redundantCerts += 1; continue; } revocation = zert; } } // it is not properly bound? error! if (selfCert == null) { ring = removeSubKey(ring, key); log.add(LogType.MSG_KC_SUB_NO_CERT, indent, KeyFormattingUtils.convertKeyIdToHex(key.getKeyID())); indent -= 1; continue; } // If we have flags, check if the algorithm supports all of them if (selfCert.getHashedSubPackets() != null && selfCert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { int flags = ((KeyFlags) selfCert.getHashedSubPackets() .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); int algo = key.getAlgorithm(); // If this is a signing key, but not a signing algorithm, warn the user if (!isSigningAlgo(algo) && (flags & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA) { log.add(LogType.MSG_KC_SUB_ALGO_BAD_SIGN, indent); } // If this is an encryption key, but not an encryption algorithm, warn the user if (!isEncryptionAlgo(algo) && ((flags & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE || (flags & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS)) { log.add(LogType.MSG_KC_SUB_ALGO_BAD_ENCRYPT, indent); } } // re-add certification modified = PGPPublicKey.addCertification(modified, selfCert); // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); } // replace pubkey in keyring ring = replacePublicKey(ring, modified); indent -= 1; } if (badCerts > 0 && redundantCerts > 0) { // multi plural would make this complex, just leaving this as is... log.add(LogType.MSG_KC_SUCCESS_BAD_AND_RED, indent, Integer.toString(badCerts), Integer.toString(redundantCerts)); } else if (badCerts > 0) { log.add(LogType.MSG_KC_SUCCESS_BAD, indent, badCerts); } else if (redundantCerts > 0) { log.add(LogType.MSG_KC_SUCCESS_REDUNDANT, indent, redundantCerts); } else { log.add(LogType.MSG_KC_SUCCESS, indent); } return isSecret() ? new CanonicalizedSecretKeyRing((PGPSecretKeyRing) ring, 1) : new CanonicalizedPublicKeyRing((PGPPublicKeyRing) ring, 0); }
From source file:org.sufficientlysecure.keychain.pgp.UncachedKeyRing.java
License:Open Source License
/** This operation merges information from a different keyring, returning a combined * UncachedKeyRing.// ww w . jav a2 s.co m * * The combined keyring contains the subkeys, user ids and user attributes of both input * keyrings, but it does not necessarily have the canonicalized property. * * @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId * @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as * this object, or null on error. * */ public UncachedKeyRing merge(UncachedKeyRing other, OperationLog log, int indent) { // This is logged in the calling method to provide more meta info // log.add(isSecret() ? LogType.MSG_MG_SECRET : LogType.MSG_MG_PUBLIC, // indent, KeyFormattingUtils.convertKeyIdToHex(getMasterKeyId())); indent += 1; long masterKeyId = other.getMasterKeyId(); if (getMasterKeyId() != masterKeyId || !Arrays.equals(getFingerprint(), other.getFingerprint())) { log.add(LogType.MSG_MG_ERROR_HETEROGENEOUS, indent); return null; } // remember which certs we already added. this is cheaper than semantic deduplication Set<byte[]> certs = new TreeSet<>(new Comparator<byte[]>() { public int compare(byte[] left, byte[] right) { // check for length equality if (left.length != right.length) { return left.length - right.length; } // compare byte-by-byte for (int i = 0; i < left.length; i++) { if (left[i] != right[i]) { return (left[i] & 0xff) - (right[i] & 0xff); } } // ok they're the same return 0; } }); try { PGPKeyRing result = mRing; PGPKeyRing candidate = other.mRing; // Pre-load all existing certificates for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(result.getPublicKeys())) { for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) { certs.add(cert.getEncoded()); } } // keep track of the number of new certs we add int newCerts = 0; for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(candidate.getPublicKeys())) { final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID()); if (resultKey == null) { log.add(LogType.MSG_MG_NEW_SUBKEY, indent); // special case: if both rings are secret, copy over the secret key if (isSecret() && other.isSecret()) { PGPSecretKey sKey = ((PGPSecretKeyRing) candidate).getSecretKey(key.getKeyID()); result = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) result, sKey); } else { // otherwise, just insert the public key result = replacePublicKey(result, key); } continue; } // Modifiable version of the old key, which we merge stuff into (keep old for comparison) PGPPublicKey modified = resultKey; // Iterate certifications for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getKeySignatures())) { // Don't merge foreign stuff into secret keys if (cert.getKeyID() != masterKeyId && isSecret()) { continue; } byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { continue; } certs.add(encoded); modified = PGPPublicKey.addCertification(modified, cert); newCerts += 1; } // If this is a subkey, merge it in and stop here if (!key.isMasterKey()) { if (modified != resultKey) { result = replacePublicKey(result, modified); } continue; } // Copy over all user id certificates for (byte[] rawUserId : new IterableIterator<byte[]>(key.getRawUserIDs())) { @SuppressWarnings("unchecked") Iterator<PGPSignature> signaturesIt = key.getSignaturesForID(rawUserId); // no signatures for this User ID, skip it if (signaturesIt == null) { continue; } for (PGPSignature cert : new IterableIterator<>(signaturesIt)) { // Don't merge foreign stuff into secret keys if (cert.getKeyID() != masterKeyId && isSecret()) { continue; } byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { continue; } newCerts += 1; certs.add(encoded); modified = PGPPublicKey.addCertification(modified, rawUserId, cert); } } // Copy over all user attribute certificates for (PGPUserAttributeSubpacketVector vector : new IterableIterator<PGPUserAttributeSubpacketVector>( key.getUserAttributes())) { @SuppressWarnings("unchecked") Iterator<PGPSignature> signaturesIt = key.getSignaturesForUserAttribute(vector); // no signatures for this user attribute attribute, skip it if (signaturesIt == null) { continue; } for (PGPSignature cert : new IterableIterator<>(signaturesIt)) { // Don't merge foreign stuff into secret keys if (cert.getKeyID() != masterKeyId && isSecret()) { continue; } byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { continue; } newCerts += 1; certs.add(encoded); modified = PGPPublicKey.addCertification(modified, vector, cert); } } // If anything change, save the updated (sub)key if (modified != resultKey) { result = replacePublicKey(result, modified); } } if (newCerts > 0) { log.add(LogType.MSG_MG_FOUND_NEW, indent, Integer.toString(newCerts)); } else { log.add(LogType.MSG_MG_UNCHANGED, indent); } return new UncachedKeyRing(result); } catch (IOException e) { log.add(LogType.MSG_MG_ERROR_ENCODE, indent); return null; } }