Example usage for org.bouncycastle.openpgp PGPSignature CERTIFICATION_REVOCATION

List of usage examples for org.bouncycastle.openpgp PGPSignature CERTIFICATION_REVOCATION

Introduction

In this page you can find the example usage for org.bouncycastle.openpgp PGPSignature CERTIFICATION_REVOCATION.

Prototype

int CERTIFICATION_REVOCATION

To view the source code for org.bouncycastle.openpgp PGPSignature CERTIFICATION_REVOCATION.

Click Source Link

Usage

From source file:com.google.e2e.bcdriver.KeyChecker.java

License:Apache License

private static final void maybeAddUserID(List<UserID> uids, PGPPublicKey pk, String uid, StringBuilder errors)
        throws PGPException, SignatureException, IOException {

    Iterator<PGPSignature> sigit = Util.getTypedIterator(pk.getSignaturesForID(uid), PGPSignature.class);
    if (sigit == null) {
        errors.append(/* ww w .j a v  a  2  s.c  o m*/
                "Reject name '" + uid + "' for " + nicePk(pk) + " because no self-signatures were found.\n");
        return;
    }

    // Select the most recent valid signature.
    PGPSignature validSig = null;
    long validTs = -1L;

    while (sigit.hasNext()) {
        PGPSignature sig = sigit.next();

        switch (sig.getSignatureType()) {
        case PGPSignature.DEFAULT_CERTIFICATION:
        case PGPSignature.NO_CERTIFICATION:
        case PGPSignature.CASUAL_CERTIFICATION:
        case PGPSignature.POSITIVE_CERTIFICATION:
        case PGPSignature.CERTIFICATION_REVOCATION:
            if (isGoodUIDSignature(sig, pk, uid, errors)) {
                long ts = sig.getCreationTime().getTime();
                if (ts > validTs) {
                    validTs = ts;
                    validSig = sig;
                }
            }
            break;

        default:
            break;
        }
    }

    if (validSig == null) {
        errors.append("Name '" + uid + "' rejected because no self-signatures were found.\n");
        return;
    }

    if (validSig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
        errors.append("Name '" + uid + "' rejected because it was revoked.\n");
        return;
    }

    // Add UID information.
    uids.add(new UserID(uid, validSig));
}

From source file:org.kontalk.certgen.PGP.java

License:Open Source License

public static PGPPublicKey revokeUserAttributes(PGPKeyPair secret, PGPPublicKey keyToBeSigned,
        PGPUserAttributeSubpacketVector attributes) throws SignatureException, PGPException {

    return PGP.signUserAttributes(secret, keyToBeSigned, attributes, PGPSignature.CERTIFICATION_REVOCATION);
}

From source file:org.sufficientlysecure.keychain.pgp.PgpKeyOperation.java

License:Open Source License

private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, int masterKeyFlags,
        long masterKeyExpiry, CryptoInputParcel cryptoInput, SaveKeyringParcel saveParcel, OperationLog log,
        int indent) {

    SecurityTokenSignOperationsBuilder nfcSignOps = new SecurityTokenSignOperationsBuilder(
            cryptoInput.getSignatureTime(), masterSecretKey.getKeyID(), masterSecretKey.getKeyID());
    SecurityTokenKeyToCardOperationsBuilder nfcKeyToCardOps = new SecurityTokenKeyToCardOperationsBuilder(
            masterSecretKey.getKeyID());

    progress(R.string.progress_modify, 0);

    PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();

    PGPPrivateKey masterPrivateKey;/*www .ja  v a  2  s.  co m*/

    if (isDivertToCard(masterSecretKey)) {
        masterPrivateKey = null;
        log.add(LogType.MSG_MF_DIVERT, indent);
    } else {

        // 1. Unlock private key
        progress(R.string.progress_modify_unlock, 10);
        log.add(LogType.MSG_MF_UNLOCK, indent);
        {
            try {
                PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
                        .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
                        .build(cryptoInput.getPassphrase().getCharArray());
                masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
            } catch (PGPException e) {
                log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }
        }
    }

    try {

        // Check if we were cancelled
        if (checkCancelled()) {
            log.add(LogType.MSG_OPERATION_CANCELLED, indent);
            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
        }

        { // work on master secret key

            PGPPublicKey modifiedPublicKey = masterPublicKey;

            // 2a. Add certificates for new user ids
            subProgressPush(15, 23);
            for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {

                progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
                String userId = saveParcel.mAddUserIds.get(i);
                log.add(LogType.MSG_MF_UID_ADD, indent, userId);

                if ("".equals(userId)) {
                    log.add(LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1);
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }

                // this operation supersedes all previous binding and revocation certificates,
                // so remove those to retain assertions from canonicalization for later operations
                @SuppressWarnings("unchecked")
                Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
                if (it != null) {
                    for (PGPSignature cert : new IterableIterator<>(it)) {
                        if (cert.getKeyID() != masterPublicKey.getKeyID()) {
                            // foreign certificate?! error error error
                            log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
                            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                        }
                        if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
                                || cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
                                || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
                                || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
                                || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
                            modifiedPublicKey = PGPPublicKey.removeCertification(modifiedPublicKey, userId,
                                    cert);
                        }
                    }
                }

                // if it's supposed to be primary, we can do that here as well
                boolean isPrimary = saveParcel.mChangePrimaryUserId != null
                        && userId.equals(saveParcel.mChangePrimaryUserId);
                // generate and add new certificate
                try {
                    PGPSignature cert = generateUserIdSignature(
                            getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(),
                            masterPrivateKey, masterPublicKey, userId, isPrimary, masterKeyFlags,
                            masterKeyExpiry);
                    modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
                } catch (NfcInteractionNeeded e) {
                    nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
                }
            }
            subProgressPop();

            // 2b. Add certificates for new user ids
            subProgressPush(23, 32);
            for (int i = 0; i < saveParcel.mAddUserAttribute.size(); i++) {

                progress(R.string.progress_modify_adduat,
                        (i - 1) * (100 / saveParcel.mAddUserAttribute.size()));
                WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i);

                switch (attribute.getType()) {
                // the 'none' type must not succeed
                case WrappedUserAttribute.UAT_NONE:
                    log.add(LogType.MSG_MF_UAT_ERROR_EMPTY, indent);
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                case WrappedUserAttribute.UAT_IMAGE:
                    log.add(LogType.MSG_MF_UAT_ADD_IMAGE, indent);
                    break;
                default:
                    log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent);
                    break;
                }

                PGPUserAttributeSubpacketVector vector = attribute.getVector();

                // generate and add new certificate
                try {
                    PGPSignature cert = generateUserAttributeSignature(
                            getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(),
                            masterPrivateKey, masterPublicKey, vector, masterKeyFlags, masterKeyExpiry);
                    modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert);
                } catch (NfcInteractionNeeded e) {
                    nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
                }
            }
            subProgressPop();

            // 2c. Add revocations for revoked user ids
            subProgressPush(32, 40);
            for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {

                progress(R.string.progress_modify_revokeuid,
                        (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
                String userId = saveParcel.mRevokeUserIds.get(i);
                log.add(LogType.MSG_MF_UID_REVOKE, indent, userId);

                // Make sure the user id exists (yes these are 10 LoC in Java!)
                boolean exists = false;
                //noinspection unchecked
                for (String uid : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
                    if (userId.equals(uid)) {
                        exists = true;
                        break;
                    }
                }
                if (!exists) {
                    log.add(LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent);
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }

                // a duplicate revocation will be removed during canonicalization, so no need to
                // take care of that here.
                try {
                    PGPSignature cert = generateRevocationSignature(
                            getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(),
                            masterPrivateKey, masterPublicKey, userId);
                    modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
                } catch (NfcInteractionNeeded e) {
                    nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
                }
            }
            subProgressPop();

            // 3. If primary user id changed, generate new certificates for both old and new
            if (saveParcel.mChangePrimaryUserId != null) {
                progress(R.string.progress_modify_primaryuid, 40);

                // keep track if we actually changed one
                boolean ok = false;
                log.add(LogType.MSG_MF_UID_PRIMARY, indent, saveParcel.mChangePrimaryUserId);
                indent += 1;

                // we work on the modifiedPublicKey here, to respect new or newly revoked uids
                // noinspection unchecked
                for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
                    boolean isRevoked = false;
                    PGPSignature currentCert = null;
                    // noinspection unchecked
                    for (PGPSignature cert : new IterableIterator<PGPSignature>(
                            modifiedPublicKey.getSignaturesForID(userId))) {
                        if (cert.getKeyID() != masterPublicKey.getKeyID()) {
                            // foreign certificate?! error error error
                            log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
                            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                        }
                        // we know from canonicalization that if there is any revocation here, it
                        // is valid and not superseded by a newer certification.
                        if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
                            isRevoked = true;
                            continue;
                        }
                        // we know from canonicalization that there is only one binding
                        // certification here, so we can just work with the first one.
                        if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
                                || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
                                || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
                                || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
                            currentCert = cert;
                        }
                    }

                    if (currentCert == null) {
                        // no certificate found?! error error error
                        log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
                        return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                    }

                    // we definitely should not update certifications of revoked keys, so just leave it.
                    if (isRevoked) {
                        // revoked user ids cannot be primary!
                        if (userId.equals(saveParcel.mChangePrimaryUserId)) {
                            log.add(LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
                            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                        }
                        continue;
                    }

                    // if this is~ the/a primary user id
                    if (currentCert.getHashedSubPackets() != null
                            && currentCert.getHashedSubPackets().isPrimaryUserID()) {
                        // if it's the one we want, just leave it as is
                        if (userId.equals(saveParcel.mChangePrimaryUserId)) {
                            ok = true;
                            continue;
                        }
                        // otherwise, generate new non-primary certification
                        log.add(LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
                        modifiedPublicKey = PGPPublicKey.removeCertification(modifiedPublicKey, userId,
                                currentCert);
                        try {
                            PGPSignature newCert = generateUserIdSignature(
                                    getSignatureGenerator(masterSecretKey, cryptoInput),
                                    cryptoInput.getSignatureTime(), masterPrivateKey, masterPublicKey, userId,
                                    false, masterKeyFlags, masterKeyExpiry);
                            modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId,
                                    newCert);
                        } catch (NfcInteractionNeeded e) {
                            nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
                        }

                        continue;
                    }

                    // if we are here, this is not currently a primary user id

                    // if it should be
                    if (userId.equals(saveParcel.mChangePrimaryUserId)) {
                        // add shiny new primary user id certificate
                        log.add(LogType.MSG_MF_PRIMARY_NEW, indent);
                        modifiedPublicKey = PGPPublicKey.removeCertification(modifiedPublicKey, userId,
                                currentCert);
                        try {
                            PGPSignature newCert = generateUserIdSignature(
                                    getSignatureGenerator(masterSecretKey, cryptoInput),
                                    cryptoInput.getSignatureTime(), masterPrivateKey, masterPublicKey, userId,
                                    true, masterKeyFlags, masterKeyExpiry);
                            modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId,
                                    newCert);
                        } catch (NfcInteractionNeeded e) {
                            nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
                        }
                        ok = true;
                    }

                    // user id is not primary and is not supposed to be - nothing to do here.

                }

                indent -= 1;

                if (!ok) {
                    log.add(LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }
            }

            // Update the secret key ring
            if (modifiedPublicKey != masterPublicKey) {
                masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
                masterPublicKey = modifiedPublicKey;
                sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
            }

        }

        // Check if we were cancelled - again
        if (checkCancelled()) {
            log.add(LogType.MSG_OPERATION_CANCELLED, indent);
            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
        }

        // 4a. For each subkey change, generate new subkey binding certificate
        subProgressPush(50, 60);
        for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {

            progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / saveParcel.mChangeSubKeys.size()));
            SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
            log.add(LogType.MSG_MF_SUBKEY_CHANGE, indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));

            PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
            if (sKey == null) {
                log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING, indent + 1,
                        KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            if (change.mDummyStrip) {
                // IT'S DANGEROUS~
                // no really, it is. this operation irrevocably removes the private key data from the key
                sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
                sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
            } else if (change.mMoveKeyToSecurityToken) {
                if (checkSecurityTokenCompatibility(sKey, log, indent + 1)) {
                    log.add(LogType.MSG_MF_KEYTOCARD_START, indent + 1,
                            KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
                    nfcKeyToCardOps.addSubkey(change.mKeyId);
                } else {
                    // Appropriate log message already set by checkSecurityTokenCompatibility
                    return new PgpEditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
                }
            } else if (change.mSecurityTokenSerialNo != null) {
                // NOTE: Does this code get executed? Or always handled in internalRestricted?
                if (change.mSecurityTokenSerialNo.length != 16) {
                    log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL, indent + 1,
                            KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }
                log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
                        KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
                        Hex.toHexString(change.mSecurityTokenSerialNo, 8, 6));
                sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mSecurityTokenSerialNo);
                sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
            }

            // This doesn't concern us any further
            if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) {
                continue;
            }

            // expiry must not be in the past
            if (change.mExpiry != null && change.mExpiry != 0
                    && new Date(change.mExpiry * 1000).before(new Date())) {
                log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, indent + 1,
                        KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            // if this is the master key, update uid certificates instead
            if (change.mKeyId == masterPublicKey.getKeyID()) {
                int flags = change.mFlags == null ? masterKeyFlags : change.mFlags;
                long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry;

                if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
                    log.add(LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }

                PGPPublicKey pKey = updateMasterCertificates(masterSecretKey, masterPrivateKey, masterPublicKey,
                        flags, expiry, cryptoInput, nfcSignOps, indent, log);
                if (pKey == null) {
                    // error log entry has already been added by updateMasterCertificates itself
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }
                masterSecretKey = PGPSecretKey.replacePublicKey(sKey, pKey);
                masterPublicKey = pKey;
                sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
                continue;
            }

            // otherwise, continue working on the public key
            PGPPublicKey pKey = sKey.getPublicKey();

            // keep old flags, or replace with new ones
            int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
            long expiry;
            if (change.mExpiry == null) {
                long valid = pKey.getValidSeconds();
                expiry = valid == 0 ? 0 : pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
            } else {
                expiry = change.mExpiry;
            }

            // drop all old signatures, they will be superseded by the new one
            //noinspection unchecked
            for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
                // special case: if there is a revocation, don't use expiry from before
                if ((change.mExpiry == null || change.mExpiry == 0L)
                        && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
                    expiry = 0;
                }
                pKey = PGPPublicKey.removeCertification(pKey, sig);
            }

            PGPPrivateKey subPrivateKey;
            if (!isDivertToCard(sKey)) {
                PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
                        .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
                        .build(cryptoInput.getPassphrase().getCharArray());
                subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
                // super special case: subkey is allowed to sign, but isn't available
                if (subPrivateKey == null) {
                    log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED, indent + 1,
                            KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
                }
            } else {
                subPrivateKey = null;
            }
            try {
                PGPSignature sig = generateSubkeyBindingSignature(
                        getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(),
                        masterPublicKey, masterPrivateKey, getSignatureGenerator(sKey, cryptoInput),
                        subPrivateKey, pKey, flags, expiry);

                // generate and add new signature
                pKey = PGPPublicKey.addCertification(pKey, sig);
                sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
            } catch (NfcInteractionNeeded e) {
                nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
            }

        }
        subProgressPop();

        // 4b. For each subkey revocation, generate new subkey revocation certificate
        subProgressPush(60, 65);
        for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) {

            progress(R.string.progress_modify_subkeyrevoke, (i - 1) * (100 / saveParcel.mRevokeSubKeys.size()));
            long revocation = saveParcel.mRevokeSubKeys.get(i);
            log.add(LogType.MSG_MF_SUBKEY_REVOKE, indent, KeyFormattingUtils.convertKeyIdToHex(revocation));

            PGPSecretKey sKey = sKR.getSecretKey(revocation);
            if (sKey == null) {
                log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING, indent + 1,
                        KeyFormattingUtils.convertKeyIdToHex(revocation));
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }
            PGPPublicKey pKey = sKey.getPublicKey();

            // generate and add new signature
            try {
                PGPSignature sig = generateRevocationSignature(
                        getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(),
                        masterPublicKey, masterPrivateKey, pKey);

                pKey = PGPPublicKey.addCertification(pKey, sig);
                sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
            } catch (NfcInteractionNeeded e) {
                nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
            }
        }
        subProgressPop();

        // 5. Generate and add new subkeys
        subProgressPush(70, 90);
        for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {

            // Check if we were cancelled - again. This operation is expensive so we do it each loop.
            if (checkCancelled()) {
                log.add(LogType.MSG_OPERATION_CANCELLED, indent);
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
            }

            progress(R.string.progress_modify_subkeyadd, (i - 1) * (100 / saveParcel.mAddSubKeys.size()));
            SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
            log.add(LogType.MSG_MF_SUBKEY_NEW, indent,
                    KeyFormattingUtils.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve));

            if (isDivertToCard(masterSecretKey)) {
                log.add(LogType.MSG_MF_ERROR_DIVERT_NEWSUB, indent + 1);
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            if (add.mExpiry == null) {
                log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent + 1);
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            if (add.mExpiry > 0L && new Date(add.mExpiry * 1000).before(new Date())) {
                log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, indent + 1);
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            // generate a new secret key (privkey only for now)
            subProgressPush((i - 1) * (100 / saveParcel.mAddSubKeys.size()),
                    i * (100 / saveParcel.mAddSubKeys.size()));
            PGPKeyPair keyPair = createKey(add, cryptoInput.getSignatureTime(), log, indent);
            subProgressPop();
            if (keyPair == null) {
                log.add(LogType.MSG_MF_ERROR_PGP, indent + 1);
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            // add subkey binding signature (making this a sub rather than master key)
            PGPPublicKey pKey = keyPair.getPublicKey();
            try {
                PGPSignature cert = generateSubkeyBindingSignature(
                        getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(),
                        masterPublicKey, masterPrivateKey, getSignatureGenerator(pKey, cryptoInput, false),
                        keyPair.getPrivateKey(), pKey, add.mFlags, add.mExpiry);
                pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
            } catch (NfcInteractionNeeded e) {
                nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
            }

            PGPSecretKey sKey;
            {
                // Build key encrypter and decrypter based on passphrase
                PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
                        .get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
                PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
                        PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
                        PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
                                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
                                .build(cryptoInput.getPassphrase().getCharArray());

                PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
                        .get(PgpSecurityConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
                sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
            }

            log.add(LogType.MSG_MF_SUBKEY_NEW_ID, indent + 1,
                    KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));

            sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);

        }
        subProgressPop();

        // Check if we were cancelled - again. This operation is expensive so we do it each loop.
        if (checkCancelled()) {
            log.add(LogType.MSG_OPERATION_CANCELLED, indent);
            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
        }

        // 6. If requested, change passphrase
        if (saveParcel.getChangeUnlockParcel() != null) {
            progress(R.string.progress_modify_passphrase, 90);
            log.add(LogType.MSG_MF_PASSPHRASE, indent);
            indent += 1;

            sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
                    saveParcel.getChangeUnlockParcel().mNewPassphrase, log, indent);
            if (sKR == null) {
                // The error has been logged above, just return a bad state
                return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
            }

            indent -= 1;
        }

        // 7. if requested, change PIN and/or Admin PIN on security token
        if (saveParcel.mSecurityTokenPin != null) {
            progress(R.string.progress_modify_pin, 90);
            log.add(LogType.MSG_MF_PIN, indent);
            indent += 1;

            nfcKeyToCardOps.setPin(saveParcel.mSecurityTokenPin);

            indent -= 1;
        }
        if (saveParcel.mSecurityTokenAdminPin != null) {
            progress(R.string.progress_modify_admin_pin, 90);
            log.add(LogType.MSG_MF_ADMIN_PIN, indent);
            indent += 1;

            nfcKeyToCardOps.setAdminPin(saveParcel.mSecurityTokenAdminPin);

            indent -= 1;
        }

    } catch (IOException e) {
        Log.e(Constants.TAG, "encountered IOException while modifying key", e);
        log.add(LogType.MSG_MF_ERROR_ENCODE, indent + 1);
        return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
    } catch (PGPException e) {
        Log.e(Constants.TAG, "encountered pgp error while modifying key", e);
        log.add(LogType.MSG_MF_ERROR_PGP, indent + 1);
        return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
    } catch (SignatureException e) {
        Log.e(Constants.TAG, "encountered SignatureException while modifying key", e);
        log.add(LogType.MSG_MF_ERROR_SIG, indent + 1);
        return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
    }

    progress(R.string.progress_done, 100);

    if (!nfcSignOps.isEmpty() && !nfcKeyToCardOps.isEmpty()) {
        log.add(LogType.MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS, indent + 1);
        return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
    }

    if (!nfcSignOps.isEmpty()) {
        log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
        return new PgpEditKeyResult(log, nfcSignOps.build(), cryptoInput);
    }

    if (!nfcKeyToCardOps.isEmpty()) {
        log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
        return new PgpEditKeyResult(log, nfcKeyToCardOps.build(), cryptoInput);
    }

    log.add(LogType.MSG_MF_SUCCESS, indent);
    return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));

}

From source file:org.sufficientlysecure.keychain.pgp.PgpKeyOperation.java

License:Open Source License

/** Update all (non-revoked) uid signatures with new flags and expiry time. */
private PGPPublicKey updateMasterCertificates(PGPSecretKey masterSecretKey, PGPPrivateKey masterPrivateKey,
        PGPPublicKey masterPublicKey, int flags, long expiry, CryptoInputParcel cryptoInput,
        SecurityTokenSignOperationsBuilder nfcSignOps, int indent, OperationLog log)
        throws PGPException, IOException, SignatureException {

    // keep track if we actually changed one
    boolean ok = false;
    log.add(LogType.MSG_MF_MASTER, indent);
    indent += 1;/*from ww w  .  j  av a 2 s. c om*/

    PGPPublicKey modifiedPublicKey = masterPublicKey;

    // we work on the modifiedPublicKey here, to respect new or newly revoked uids
    // noinspection unchecked
    for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
        boolean isRevoked = false;
        PGPSignature currentCert = null;
        // noinspection unchecked
        for (PGPSignature cert : new IterableIterator<PGPSignature>(
                modifiedPublicKey.getSignaturesForID(userId))) {
            if (cert.getKeyID() != masterPublicKey.getKeyID()) {
                // foreign certificate?! error error error
                log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
                return null;
            }
            // we know from canonicalization that if there is any revocation here, it
            // is valid and not superseded by a newer certification.
            if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
                isRevoked = true;
                continue;
            }
            // we know from canonicalization that there is only one binding
            // certification here, so we can just work with the first one.
            if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
                    || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
                    || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
                    || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
                currentCert = cert;
            }
        }

        if (currentCert == null) {
            // no certificate found?! error error error
            log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
            return null;
        }

        // we definitely should not update certifications of revoked keys, so just leave it.
        if (isRevoked) {
            continue;
        }

        // add shiny new user id certificate
        boolean isPrimary = currentCert.getHashedSubPackets() != null
                && currentCert.getHashedSubPackets().isPrimaryUserID();
        modifiedPublicKey = PGPPublicKey.removeCertification(modifiedPublicKey, userId, currentCert);
        try {
            PGPSignature newCert = generateUserIdSignature(getSignatureGenerator(masterSecretKey, cryptoInput),
                    cryptoInput.getSignatureTime(), masterPrivateKey, masterPublicKey, userId, isPrimary, flags,
                    expiry);
            modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, newCert);
        } catch (NfcInteractionNeeded e) {
            nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
        }
        ok = true;

    }

    if (!ok) {
        // might happen, theoretically, if there is a key with no uid..
        log.add(LogType.MSG_MF_ERROR_MASTER_NONE, indent);
        return null;
    }

    return modifiedPublicKey;

}

From source file:org.sufficientlysecure.keychain.pgp.PgpKeyOperation.java

License:Open Source License

private static PGPSignature generateRevocationSignature(PGPSignatureGenerator sGen, Date creationTime,
        PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)

        throws IOException, PGPException, SignatureException {
    PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
    // we use the tag NO_REASON since gnupg does not care about the tag while verifying
    // signatures with a revoked key, the warning is the same
    subHashedPacketsGen.setRevocationReason(true, RevocationReasonTags.NO_REASON, "");
    subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
    sGen.setHashedSubpackets(subHashedPacketsGen.generate());
    sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
    return sGen.generateCertification(userId, pKey);
}

From source file:org.sufficientlysecure.keychain.pgp.PgpKeyOperationTest.java

License:Open Source License

@Test
public void testUserIdRevoke() throws Exception {

    UncachedKeyRing modified;/*from  w w w  .j a v  a2s . com*/
    String uid = ring.getPublicKey().getUnorderedUserIds().get(1);

    { // revoke second user id

        parcel.mRevokeUserIds.add(uid);

        modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);

        Assert.assertEquals("no extra packets in original", 0, onlyA.size());
        Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());

        Packet p;

        p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
        Assert.assertTrue("first new packet must be secret subkey", p instanceof SignaturePacket);
        Assert.assertEquals("signature type must be subkey binding certificate",
                PGPSignature.CERTIFICATION_REVOCATION, ((SignaturePacket) p).getSignatureType());
        Assert.assertEquals("signature must have been created by master key", ring.getMasterKeyId(),
                ((SignaturePacket) p).getKeyID());

    }

    { // re-add second user id

        parcel.reset();
        parcel.mChangePrimaryUserId = uid;

        assertModifyFailure("setting primary user id to a revoked user id should fail", modified, parcel,
                LogType.MSG_MF_ERROR_REVOKED_PRIMARY);

    }

    { // re-add second user id

        parcel.reset();
        parcel.mAddUserIds.add(uid);

        applyModificationWithChecks(parcel, modified, onlyA, onlyB);

        Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
        Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());

        Packet p;

        p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
        Assert.assertTrue("first outdated packet must be signature", p instanceof SignaturePacket);
        Assert.assertEquals("first outdated signature type must be positive certification",
                PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
        Assert.assertEquals("first outdated signature must have been created by master key",
                ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());

        p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
        Assert.assertTrue("second outdated packet must be signature", p instanceof SignaturePacket);
        Assert.assertEquals("second outdated signature type must be certificate revocation",
                PGPSignature.CERTIFICATION_REVOCATION, ((SignaturePacket) p).getSignatureType());
        Assert.assertEquals("second outdated signature must have been created by master key",
                ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());

        p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
        Assert.assertTrue("new packet must be signature ", p instanceof SignaturePacket);
        Assert.assertEquals("new signature type must be positive certification",
                PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
        Assert.assertEquals("signature must have been created by master key", ring.getMasterKeyId(),
                ((SignaturePacket) p).getKeyID());
    }

    { // revocation of non-existent user id should fail
        parcel.reset();
        parcel.mRevokeUserIds.add("nonexistent");

        assertModifyFailure("revocation of nonexistent user id should fail", modified, parcel,
                LogType.MSG_MF_ERROR_NOEXIST_REVOKE);
    }

}

From source file:org.sufficientlysecure.keychain.pgp.UncachedKeyRing.java

License:Open Source License

/** "Canonicalizes" a public key, removing inconsistencies in the process.
 *
 * More specifically://from www.  j a va2s.c o m
 *  - 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.UncachedKeyringCanonicalizeTest.java

License:Open Source License

@Test
public void testUidRevocationOutdated() throws Exception {
    // an older uid revocation cert should be rejected
    subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() - 1000 * 1000));
    PGPSignature revocation = forgeSignature(secretKey, PGPSignature.CERTIFICATION_REVOCATION,
            subHashedPacketsGen, "twi", secretKey.getPublicKey());

    injectEverywhere(ring, revocation.getEncoded());

}

From source file:org.sufficientlysecure.keychain.pgp.UncachedKeyringCanonicalizeTest.java

License:Open Source License

@Test
public void testUidRevocationRedundant() throws Exception {

    PGPSignature revocation = forgeSignature(secretKey, PGPSignature.CERTIFICATION_REVOCATION,
            subHashedPacketsGen, "twi", secretKey.getPublicKey());

    // add that revocation to the base, and check if the redundant one will be rejected as well
    UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 2);

    injectEverywhere(modified, revocation.getEncoded());

    // an older (but different!) uid revocation should be rejected as well
    subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() - 1000 * 1000));
    revocation = forgeSignature(secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi",
            secretKey.getPublicKey());/*from  www . ja  v a  2 s.co  m*/

    injectEverywhere(modified, revocation.getEncoded());

}

From source file:org.sufficientlysecure.keychain.pgp.UncachedPublicKey.java

License:Open Source License

/** Returns the primary user id, as indicated by the public key's self certificates.
 *
 * This is an expensive operation, since potentially a lot of certificates (and revocations)
 * have to be checked, and even then the result is NOT guaranteed to be constant through a
 * canonicalization operation./*from w w  w.j  ava2s .  co m*/
 *
 * Returns null if there is no primary user id (as indicated by certificates)
 *
 */
public String getPrimaryUserId() {
    byte[] found = null;
    PGPSignature foundSig = null;
    // noinspection unchecked
    for (byte[] rawUserId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
        PGPSignature revocation = null;

        @SuppressWarnings("unchecked")
        Iterator<PGPSignature> signaturesIt = mPublicKey.getSignaturesForID(rawUserId);
        // no signatures for this User ID
        if (signaturesIt == null) {
            continue;
        }

        for (PGPSignature sig : new IterableIterator<>(signaturesIt)) {
            try {

                // if this is a revocation, this is not the user id
                if (sig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
                    // make sure it's actually valid
                    sig.init(new JcaPGPContentVerifierBuilderProvider()
                            .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
                    if (!sig.verifyCertification(rawUserId, mPublicKey)) {
                        continue;
                    }
                    if (found != null && Arrays.equals(found, rawUserId)) {
                        found = null;
                    }
                    revocation = sig;
                    // this revocation may still be overridden by a newer cert
                    continue;
                }

                if (sig.getHashedSubPackets() != null && sig.getHashedSubPackets().isPrimaryUserID()) {
                    if (foundSig != null && sig.getCreationTime().before(foundSig.getCreationTime())) {
                        continue;
                    }
                    // ignore if there is a newer revocation for this user id
                    if (revocation != null && sig.getCreationTime().before(revocation.getCreationTime())) {
                        continue;
                    }
                    // make sure it's actually valid
                    sig.init(new JcaPGPContentVerifierBuilderProvider()
                            .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
                    if (sig.verifyCertification(rawUserId, mPublicKey)) {
                        found = rawUserId;
                        foundSig = sig;
                        // this one can't be relevant anymore at this point
                        revocation = null;
                    }
                }

            } catch (Exception e) {
                // nothing bad happens, the key is just not considered the primary key id
            }
        }
    }
    if (found != null) {
        return Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(found);
    } else {
        return null;
    }
}