List of usage examples for org.bouncycastle.openpgp PGPSignature SUBKEY_REVOCATION
int SUBKEY_REVOCATION
To view the source code for org.bouncycastle.openpgp PGPSignature SUBKEY_REVOCATION.
Click Source Link
From source file:com.google.e2e.bcdriver.KeyChecker.java
License:Apache License
private static final void maybeAddSubkey(List<Subkey> subkeys, PGPPublicKey masterpk, PGPPublicKey subkey, StringBuilder errors) throws PGPException, SignatureException, IOException { Iterator<PGPSignature> sigit = Util.getTypedIterator(subkey.getSignatures(), PGPSignature.class); if (sigit == null) { errors.append("Reject subkey " + nicePk(subkey) + " because no binding signatures were found.\n"); return;//from w ww. j ava2s . c o m } PGPSignature validSig = null; long validTs = -1L; while (sigit.hasNext()) { PGPSignature sig = sigit.next(); switch (sig.getSignatureType()) { case PGPSignature.SUBKEY_BINDING: case PGPSignature.SUBKEY_REVOCATION: if (isGoodSubkeySignature(sig, masterpk, subkey, errors)) { if (sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) { // Reject this subkey permanently. errors.append("Subkey " + nicePk(subkey) + " revoked by " + niceSig(sig) + "\n"); return; } // signing subkeys must have an embedded back signature. if (!Util.hasKeyFlag(sig, KeyFlags.SIGN_DATA) || isGoodBackSignature(sig, masterpk, subkey, errors)) { long ts = getSignatureTimestamp(sig, errors); if (ts > validTs) { validSig = sig; validTs = ts; } } } break; default: errors.append("Ignore " + niceSig(sig) + " for subkey " + nicePk(subkey) + "\n"); break; } } // We need atleast one good binding. if (validSig == null) { errors.append( "Subkey " + nicePk(subkey) + " rejected because no valid binding signatures were found.\n"); return; } subkeys.add(new Subkey(subkey, validSig)); }
From source file:org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey.java
License:Open Source License
public boolean isRevoked() { return mPublicKey .getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION : PGPSignature.SUBKEY_REVOCATION) .hasNext(); }
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;//from w w w . ja va 2 s . c o 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
private static PGPSignature generateRevocationSignature(PGPSignatureGenerator sGen, Date creationTime, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey) 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()); // Generate key revocation or subkey revocation, depending on master/subkey-ness if (masterPublicKey.getKeyID() == pKey.getKeyID()) { sGen.init(PGPSignature.KEY_REVOCATION, masterPrivateKey); return sGen.generateCertification(masterPublicKey); } else {//from w w w.jav a2s. c o m sGen.init(PGPSignature.SUBKEY_REVOCATION, masterPrivateKey); return sGen.generateCertification(masterPublicKey, pKey); } }
From source file:org.sufficientlysecure.keychain.pgp.PgpKeyOperationTest.java
License:Open Source License
@Test public void testSubkeyRevoke() throws Exception { long keyId = KeyringTestingHelper.getSubkeyId(ring, 1); int flags = ring.getPublicKey(keyId).getKeyUsage(); UncachedKeyRing modified;//from w ww .ja va 2 s . co m { parcel.reset(); parcel.mRevokeSubKeys.add(123L); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0); UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, cryptoInput, parcel).getRing(); Assert.assertNull("revoking a nonexistent subkey should fail", otherModified); } { // revoked second subkey parcel.reset(); parcel.mRevokeSubKeys.add(keyId); modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, new CryptoInputParcel(new Date(), passphrase)); 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.SUBKEY_REVOCATION, ((SignaturePacket) p).getSignatureType()); Assert.assertEquals("signature must have been created by master key", ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID()); Assert.assertTrue("subkey must actually be revoked", modified.getPublicKey(keyId).isMaybeRevoked()); } { // re-add second subkey parcel.reset(); // re-certify the revoked subkey parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true)); modified = 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 subkey binding certification", PGPSignature.SUBKEY_BINDING, ((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 subkey revocation", PGPSignature.SUBKEY_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 subkey binding certification", PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType()); Assert.assertEquals("signature must have been created by master key", ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID()); Assert.assertFalse("subkey must no longer be revoked", modified.getPublicKey(keyId).isMaybeRevoked()); Assert.assertEquals("subkey must have the same usage flags as before", flags, (long) modified.getPublicKey(keyId).getKeyUsage()); } }
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 w w w .j a v a 2s .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 testSubkeyBindingRedundant() throws Exception { UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 2); subHashedPacketsGen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS); PGPSignature sig2 = forgeSignature(secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, secretKey.getPublicKey(), pKey.getPublicKey()); subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() - 1000 * 1000)); PGPSignature sig1 = forgeSignature(secretKey, PGPSignature.SUBKEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey(), pKey.getPublicKey()); subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() - 100 * 1000)); PGPSignature sig3 = forgeSignature(secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, secretKey.getPublicKey(), pKey.getPublicKey()); UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig1.getEncoded(), 10); modified = KeyringTestingHelper.injectPacket(modified, sig2.getEncoded(), 11); modified = KeyringTestingHelper.injectPacket(modified, sig1.getEncoded(), 12); modified = KeyringTestingHelper.injectPacket(modified, sig3.getEncoded(), 13); // canonicalize, and check if we lose the bad signature CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0); Assert.assertTrue("subkey binding signature should be gone after canonicalization", KeyringTestingHelper.diffKeyrings(modified.getEncoded(), canonicalized.getEncoded(), onlyA, onlyB)); Assert.assertEquals("canonicalized keyring should have lost two packets", 3, onlyA.size()); Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size()); Assert.assertEquals("first missing packet should be the subkey", PacketTags.SIGNATURE, onlyA.get(0).tag); Assert.assertEquals("second missing packet should be a signature", PacketTags.SIGNATURE, onlyA.get(1).tag); Assert.assertEquals("second missing packet should be a signature", PacketTags.SIGNATURE, onlyA.get(2).tag); }
From source file:org.sufficientlysecure.keychain.pgp.UncachedPublicKey.java
License:Open Source License
/** The revocation signature is NOT checked here, so this may be false! */ public boolean isMaybeRevoked() { return mPublicKey .getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION : PGPSignature.SUBKEY_REVOCATION) .hasNext();//w ww . j a v a 2 s. c o m }