Example usage for org.bouncycastle.openpgp PGPPublicKey getUserAttributes

List of usage examples for org.bouncycastle.openpgp PGPPublicKey getUserAttributes

Introduction

In this page you can find the example usage for org.bouncycastle.openpgp PGPPublicKey getUserAttributes.

Prototype

public Iterator<PGPUserAttributeSubpacketVector> getUserAttributes() 

Source Link

Document

Return any user attribute vectors associated with the key.

Usage

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;
    }

}