aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main
diff options
context:
space:
mode:
authormar-v-in <github@rvin.mooo.com>2014-06-04 21:32:37 +0200
committermar-v-in <github@rvin.mooo.com>2014-06-04 21:32:37 +0200
commitcae0071342e746c934490298c3dd3ee230a2ee32 (patch)
tree5c540d7c8ad84b462fd3cbc87b0aea7c5513fc9c /OpenKeychain/src/main
parent6a637462782b4ce57ecf154edf0974114181b8ad (diff)
parent52f1f30846ad7efa6e6ae11ed96f5b68626bfb3b (diff)
downloadopen-keychain-cae0071342e746c934490298c3dd3ee230a2ee32.tar.gz
open-keychain-cae0071342e746c934490298c3dd3ee230a2ee32.tar.bz2
open-keychain-cae0071342e746c934490298c3dd3ee230a2ee32.zip
Merge branch 'master' into automatic-contact-discovery
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
Diffstat (limited to 'OpenKeychain/src/main')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java70
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java109
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java274
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java124
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java235
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java346
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java171
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java197
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java39
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java192
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java200
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java141
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java339
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java173
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java128
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java96
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java158
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java64
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java32
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java72
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java134
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java80
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java19
-rw-r--r--OpenKeychain/src/main/res/raw-ar/help_about.html (renamed from OpenKeychain/src/main/res/raw-fa-rIR/help_about.html)1
-rw-r--r--OpenKeychain/src/main/res/raw-ar/help_changelog.html (renamed from OpenKeychain/src/main/res/raw-cs-rCZ/help_changelog.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-ar/help_nfc_beam.html (renamed from OpenKeychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-ar/help_start.html (renamed from OpenKeychain/src/main/res/raw-nl-rNL/help_start.html)6
-rw-r--r--OpenKeychain/src/main/res/raw-ar/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-ar/nfc_beam_share.html (renamed from OpenKeychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_about.html (renamed from OpenKeychain/src/main/res/raw-nl-rNL/help_about.html)1
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_changelog.html (renamed from OpenKeychain/src/main/res/raw-fa-rIR/help_changelog.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html (renamed from OpenKeychain/src/main/res/raw-fa-rIR/help_nfc_beam.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_start.html (renamed from OpenKeychain/src/main/res/raw-cs-rCZ/help_start.html)6
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html (renamed from OpenKeychain/src/main/res/raw-fa-rIR/nfc_beam_share.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_about.html3
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_changelog.html92
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_nfc_beam.html10
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_start.html22
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-de/nfc_beam_share.html8
-rw-r--r--OpenKeychain/src/main/res/raw-el/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-el/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-el/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_changelog.html8
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-et/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-et/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-et/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-fa-rIR/help_start.html24
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_changelog.html10
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_about.html (renamed from OpenKeychain/src/main/res/raw-it-rIT/help_about.html)1
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_changelog.html (renamed from OpenKeychain/src/main/res/raw-it-rIT/help_changelog.html)12
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_nfc_beam.html (renamed from OpenKeychain/src/main/res/raw-it-rIT/help_nfc_beam.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_start.html (renamed from OpenKeychain/src/main/res/raw-it-rIT/help_start.html)8
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-it/nfc_beam_share.html (renamed from OpenKeychain/src/main/res/raw-it-rIT/nfc_beam_share.html)0
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_changelog.html8
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_start.html8
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-ko/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-ko/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-ko/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-nl-rNL/help_changelog.html156
-rw-r--r--OpenKeychain/src/main/res/raw-nl-rNL/help_nfc_beam.html12
-rw-r--r--OpenKeychain/src/main/res/raw-nl-rNL/nfc_beam_share.html11
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_about.html (renamed from OpenKeychain/src/main/res/raw-cs-rCZ/help_about.html)33
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_changelog.html156
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_nfc_beam.html12
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_start.html22
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-nl/nfc_beam_share.html11
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_changelog.html2
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_changelog.html10
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_start.html8
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_changelog.html10
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_changelog.html14
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_start.html8
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_about.html1
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_start.html6
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_wot.html17
-rw-r--r--OpenKeychain/src/main/res/raw-zh/nfc_beam_share.html2
-rw-r--r--OpenKeychain/src/main/res/values-ar/strings.xml (renamed from OpenKeychain/src/main/res/values-fa-rIR/strings.xml)0
-rw-r--r--OpenKeychain/src/main/res/values-cs-rCZ/strings.xml48
-rw-r--r--OpenKeychain/src/main/res/values-cs/strings.xml31
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml7
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml6
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml6
-rw-r--r--OpenKeychain/src/main/res/values-it/strings.xml (renamed from OpenKeychain/src/main/res/values-it-rIT/strings.xml)5
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml6
-rw-r--r--OpenKeychain/src/main/res/values-nl-rNL/strings.xml198
-rw-r--r--OpenKeychain/src/main/res/values-nl/strings.xml474
-rw-r--r--OpenKeychain/src/main/res/values-pl/strings.xml3
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml56
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml5
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml68
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml1
142 files changed, 4001 insertions, 2311 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
index 31b88856f..16ef28311 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -31,6 +31,7 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -51,14 +52,15 @@ public class ExportHelper {
public void deleteKey(Uri dataUri, Handler deleteHandler) {
try {
- long masterKeyId = new ProviderHelper(mActivity).extractOrGetMasterKeyId(dataUri);
+ long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index ea4f5948e..b1df7886d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -21,18 +21,10 @@ import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
-import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
@@ -57,8 +49,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean mSelected;
- private byte[] mBytes = new byte[]{};
-
public int describeContents() {
return 0;
}
@@ -77,8 +67,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0));
dest.writeByte((byte) (mSelected ? 1 : 0));
- dest.writeInt(mBytes.length);
- dest.writeByteArray(mBytes);
dest.writeString(mExtraData);
dest.writeString(mOrigin);
}
@@ -99,8 +87,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1;
vr.mSelected = source.readByte() == 1;
- vr.mBytes = new byte[source.readInt()];
- source.readByteArray(vr.mBytes);
vr.mExtraData = source.readString();
vr.mOrigin = source.readString();
@@ -116,14 +102,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
return keyIdHex;
}
- public byte[] getBytes() {
- return mBytes;
- }
-
- public void setBytes(byte[] bytes) {
- this.mBytes = bytes;
- }
-
public boolean isSelected() {
return mSelected;
}
@@ -255,53 +233,25 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
* Constructor based on key object, used for import from NFC, QR Codes, files
*/
@SuppressWarnings("unchecked")
- public ImportKeysListEntry(Context context, PGPKeyRing pgpKeyRing) {
- // save actual key object into entry, used to import it later
- try {
- this.mBytes = pgpKeyRing.getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e);
- }
-
+ public ImportKeysListEntry(Context context, UncachedKeyRing ring) {
// selected is default
this.mSelected = true;
- if (pgpKeyRing instanceof PGPSecretKeyRing) {
- secretKey = true;
- } else {
- secretKey = false;
- }
- PGPPublicKey key = pgpKeyRing.getPublicKey();
+ secretKey = ring.isSecret();
+ UncachedPublicKey key = ring.getPublicKey();
+
+ mPrimaryUserId = key.getPrimaryUserId();
+ userIds = key.getUnorderedUserIds();
- userIds = new ArrayList<String>();
- for (String userId : new IterableIterator<String>(key.getUserIDs())) {
- userIds.add(userId);
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignaturesForID(userId))) {
- if (sig.getHashedSubPackets() != null
- && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
- try {
- // make sure it's actually valid
- sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME), key);
- if (sig.verifyCertification(userId, key)) {
- mPrimaryUserId = userId;
- }
- } catch (Exception e) {
- // nothing bad happens, the key is just not considered the primary key id
- }
- }
-
- }
- }
// if there was no user id flagged as primary, use the first one
if (mPrimaryUserId == null) {
mPrimaryUserId = userIds.get(0);
}
- this.keyId = key.getKeyID();
+ this.keyId = key.getKeyId();
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
- this.revoked = key.isRevoked();
+ this.revoked = key.maybeRevoked();
this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.bitStrength = key.getBitStrength();
final int algorithm = key.getAlgorithm();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
new file mode 100644
index 000000000..3d3b6339a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
@@ -0,0 +1,51 @@
+package org.sufficientlysecure.keychain.keyimport;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+
+import java.io.IOException;
+
+/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists
+ * for the sole purpose of keeping spongycastle and android imports in separate packages.
+ */
+public class ParcelableKeyRing implements Parcelable {
+
+ final byte[] mBytes;
+ final String mExpectedFingerprint;
+
+ public ParcelableKeyRing(byte[] bytes) {
+ mBytes = bytes;
+ mExpectedFingerprint = null;
+ }
+ public ParcelableKeyRing(byte[] bytes, String expectedFingerprint) {
+ mBytes = bytes;
+ mExpectedFingerprint = expectedFingerprint;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mBytes);
+ dest.writeString(mExpectedFingerprint);
+ }
+
+ public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() {
+ public ParcelableKeyRing createFromParcel(final Parcel source) {
+ return new ParcelableKeyRing(source.createByteArray());
+ }
+
+ public ParcelableKeyRing[] newArray(final int size) {
+ return new ParcelableKeyRing[size];
+ }
+ };
+
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public UncachedKeyRing getUncachedKeyRing() throws PgpGeneralException, IOException {
+ return UncachedKeyRing.decodeFromData(mBytes);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
new file mode 100644
index 000000000..dc0c722b9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -0,0 +1,36 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+
+/** An abstract KeyRing.
+ *
+ * This is an abstract class for all KeyRing constructs. It serves as a common
+ * denominator of available information, two implementations wrapping the same
+ * keyring should in all cases agree on the output of all methods described
+ * here.
+ *
+ * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
+ *
+ */
+public abstract class KeyRing {
+
+ abstract public long getMasterKeyId() throws PgpGeneralException;
+
+ abstract public String getPrimaryUserId() throws PgpGeneralException;
+
+ abstract public boolean isRevoked() throws PgpGeneralException;
+
+ abstract public boolean canCertify() throws PgpGeneralException;
+
+ abstract public long getEncryptId() throws PgpGeneralException;
+
+ abstract public boolean hasEncrypt() throws PgpGeneralException;
+
+ abstract public long getSignId() throws PgpGeneralException;
+
+ abstract public boolean hasSign() throws PgpGeneralException;
+
+ abstract public int getVerified() throws PgpGeneralException;
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index 86fba979c..591ccdc8e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -21,49 +21,25 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureList;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
-
public class PgpConversionHelper {
/**
- * Convert from byte[] to PGPKeyRing
- *
- * @param keysBytes
- * @return
- */
- public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) {
- PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
- PGPKeyRing keyRing = null;
- try {
- if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
- Log.e(Constants.TAG, "No keys given!");
- }
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
- }
-
- return keyRing;
- }
-
- /**
* Convert from byte[] to ArrayList<PGPSecretKey>
*
* @param keysBytes
* @return
*/
- public static ArrayList<PGPSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
+ public static ArrayList<UncachedSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
Object obj = null;
- ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
+ ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
try {
while ((obj = factory.nextObject()) != null) {
PGPSecretKey secKey = null;
@@ -72,7 +48,7 @@ public class PgpConversionHelper {
if (secKey == null) {
Log.e(Constants.TAG, "No keys given!");
}
- keys.add(secKey);
+ keys.add(new UncachedSecretKey(secKey));
} else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
PGPSecretKeyRing keyRing = null;
keyRing = (PGPSecretKeyRing) obj;
@@ -82,7 +58,7 @@ public class PgpConversionHelper {
@SuppressWarnings("unchecked")
Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
while (itr.hasNext()) {
- keys.add(itr.next());
+ keys.add(new UncachedSecretKey(itr.next()));
}
}
}
@@ -100,7 +76,7 @@ public class PgpConversionHelper {
* @param keyBytes
* @return
*/
- public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
+ public static UncachedSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keyBytes);
Object obj = null;
try {
@@ -121,80 +97,7 @@ public class PgpConversionHelper {
secKey = keyRing.getSecretKey();
}
- return secKey;
- }
-
- /**
- * Convert from byte[] to PGPSignature
- *
- * @param sigBytes
- * @return
- */
- public static PGPSignature BytesToPGPSignature(byte[] sigBytes) {
- PGPObjectFactory factory = new PGPObjectFactory(sigBytes);
- PGPSignatureList signatures = null;
- try {
- if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
- Log.e(Constants.TAG, "No signatures given!");
- return null;
- }
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
- return null;
- }
-
- return signatures.get(0);
- }
-
- /**
- * Convert from ArrayList<PGPSecretKey> to byte[]
- *
- * @param keys
- * @return
- */
- public static byte[] PGPSecretKeyArrayListToBytes(ArrayList<PGPSecretKey> keys) {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- for (PGPSecretKey key : keys) {
- try {
- key.encode(os);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting ArrayList<PGPSecretKey> to byte[]!", e);
- }
- }
-
- return os.toByteArray();
- }
-
- /**
- * Convert from PGPSecretKey to byte[]
- *
- * @param key
- * @return
- */
- public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
- try {
- return key.getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Encoding failed", e);
-
- return null;
- }
- }
-
- /**
- * Convert from PGPSecretKeyRing to byte[]
- *
- * @param keyRing
- * @return
- */
- public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
- try {
- return keyRing.getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Encoding failed", e);
-
- return null;
- }
+ return new UncachedSecretKey(secKey);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 506c161ba..c009d1b5c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,10 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
-import android.net.Uri;
-
import org.spongycastle.bcpg.ArmoredInputStream;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
@@ -31,29 +28,19 @@ import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPOnePassSignatureList;
import org.spongycastle.openpgp.PGPPBEEncryptedData;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
@@ -67,7 +54,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
/**
@@ -248,7 +234,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
- PGPSecretKey secretEncryptionKey = null;
+ WrappedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@@ -260,15 +246,12 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- long masterKeyId;
- PGPSecretKeyRing secretKeyRing;
+ WrappedSecretKeyRing secretKeyRing;
try {
- // get master key id for this encryption key id
- masterKeyId = mProviderHelper.getMasterKeyId(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
- );
// get actual keyring object based on master key id
- secretKeyRing = mProviderHelper.getPGPSecretKeyRing(masterKeyId);
+ secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
+ );
} catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop
continue;
@@ -278,13 +261,14 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
+ secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
}
/* secret key exists in database! */
+ long masterKeyId = secretEncryptionKey.getRing().getMasterKeyId();
// allow only specific keys for decryption?
if (mAllowedKeyIds != null) {
@@ -359,23 +343,17 @@ public class PgpDecryptVerify {
} else if (asymmetricPacketFound) {
currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
- PGPPrivateKey privateKey;
try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- mPassphrase.toCharArray());
- privateKey = secretEncryptionKey.extractPrivateKey(keyDecryptor);
- } catch (PGPException e) {
- throw new WrongPassphraseException();
- }
- if (privateKey == null) {
+ if (!secretEncryptionKey.unlock(mPassphrase)) {
+ throw new WrongPassphraseException();
+ }
+ } catch(PgpGeneralException e) {
throw new KeyExtractionException();
}
currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
+ PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
@@ -388,10 +366,10 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
- PGPOnePassSignature signature = null;
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- PGPPublicKey signatureKey = null;
int signatureIndex = -1;
+ WrappedPublicKeyRing signingRing = null;
+ WrappedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@@ -403,6 +381,8 @@ public class PgpDecryptVerify {
currentProgress += 10;
}
+ PGPOnePassSignature signature = null;
+
if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
@@ -410,19 +390,13 @@ public class PgpDecryptVerify {
// go through all signatures
// and find out for which signature we have a key in our database
- Long masterKeyId = null;
- String primaryUserId = null;
for (int i = 0; i < sigList.size(); ++i) {
try {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(sigList.get(i).getKeyID()));
- Map<String, Object> data = mProviderHelper.getGenericData(uri,
- new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
- new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
- ProviderHelper.FIELD_TYPE_STRING }
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
- primaryUserId = (String) data.get(KeyRings.USER_ID);
+ signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -430,43 +404,24 @@ public class PgpDecryptVerify {
}
}
- if (masterKeyId != null) {
+ if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
- PGPPublicKeyRing publicKeyRing = null;
- try {
- publicKeyRing = mProviderHelper
- .getPGPPublicKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- // can't happen
- }
-
- // get the subkey which has been used to generate this signature
- signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
-
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
- signatureResultBuilder.userId(primaryUserId);
- signatureResultBuilder.keyId(masterKeyId);
+ signatureResultBuilder.keyId(signingRing.getMasterKeyId());
+ try {
+ signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ } catch(PgpGeneralException e) {
+ Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
+ }
+ signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- // get certification status of this key
- boolean isSignatureKeyCertified;
- try {
- Object data = mProviderHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
- KeyRings.VERIFIED,
- ProviderHelper.FIELD_TYPE_INTEGER);
- isSignatureKeyCertified = ((Long) data > 0);
- } catch (ProviderHelper.NotFoundException e) {
- isSignatureKeyCertified = false;
- }
- signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@@ -541,7 +496,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
- boolean validKeyBinding = verifyKeyBinding(messageSignature, signatureKey);
+ boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@@ -617,22 +572,19 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
+ WrappedPublicKeyRing signingRing = null;
+ WrappedPublicKey signingKey = null;
+ int signatureIndex = -1;
+
// go through all signatures
// and find out for which signature we have a key in our database
- Long masterKeyId = null;
- String primaryUserId = null;
- int signatureIndex = 0;
for (int i = 0; i < sigList.size(); ++i) {
try {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(sigList.get(i).getKeyID()));
- Map<String, Object> data = mProviderHelper.getGenericData(uri,
- new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
- new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
- ProviderHelper.FIELD_TYPE_STRING }
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
- primaryUserId = (String) data.get(KeyRings.USER_ID);
+ signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -641,44 +593,25 @@ public class PgpDecryptVerify {
}
PGPSignature signature = null;
- PGPPublicKey signatureKey = null;
- if (masterKeyId != null) {
+
+ if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
- PGPPublicKeyRing publicKeyRing = null;
- try {
- publicKeyRing = mProviderHelper
- .getPGPPublicKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- // can't happen
- }
-
- // get the subkey which has been used to generate this signature
- signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
-
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
- signatureResultBuilder.userId(primaryUserId);
- signatureResultBuilder.keyId(masterKeyId);
+ signatureResultBuilder.keyId(signingRing.getMasterKeyId());
+ try {
+ signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ } catch(PgpGeneralException e) {
+ Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
+ }
+ signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- // get certification status of this key
- boolean isSignatureKeyCertified;
- try {
- Object data = mProviderHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
- KeyRings.VERIFIED,
- ProviderHelper.FIELD_TYPE_INTEGER);
- isSignatureKeyCertified = ((Long) data > 0);
- } catch (ProviderHelper.NotFoundException e) {
- isSignatureKeyCertified = false;
- }
- signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@@ -710,7 +643,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
- boolean validKeyBinding = verifyKeyBinding(signature, signatureKey);
+ boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@@ -722,113 +655,6 @@ public class PgpDecryptVerify {
return result;
}
- private boolean verifyKeyBinding(PGPSignature signature, PGPPublicKey signatureKey) {
- long signatureKeyId = signature.getKeyID();
- boolean validKeyBinding = false;
-
- PGPPublicKey mKey = null;
- try {
- PGPPublicKeyRing signKeyRing = mProviderHelper.getPGPPublicKeyRingWithKeyId(
- signatureKeyId);
- mKey = signKeyRing.getPublicKey();
- } catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found");
- }
-
- if (signature.getKeyID() != mKey.getKeyID()) {
- validKeyBinding = verifyKeyBinding(mKey, signatureKey);
- } else { //if the key used to make the signature was the master key, no need to check binding sigs
- validKeyBinding = true;
- }
- return validKeyBinding;
- }
-
- private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
- boolean validSubkeyBinding = false;
- boolean validTempSubkeyBinding = false;
- boolean validPrimaryKeyBinding = false;
-
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
-
- while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
- //gpg has an invalid subkey binding error on key import I think, but doesn't shout
- //about keys without subkey signing. Can't get it to import a slightly broken one
- //either, so we will err on bad subkey binding here.
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == masterPublicKey.getKeyID() &&
- sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
- //check and if ok, check primary key binding.
- try {
- sig.init(contentVerifierBuilderProvider, masterPublicKey);
- validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
-
- if (validTempSubkeyBinding) {
- validSubkeyBinding = true;
- }
- if (validTempSubkeyBinding) {
- validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
- masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
- masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- }
- }
- }
- return (validSubkeyBinding & validPrimaryKeyBinding);
- }
-
- private boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
- PGPPublicKey masterPublicKey,
- PGPPublicKey signingPublicKey) {
- boolean validPrimaryKeyBinding = false;
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureList eSigList;
-
- if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
- try {
- eSigList = pkts.getEmbeddedSignatures();
- } catch (IOException e) {
- return false;
- } catch (PGPException e) {
- return false;
- }
- for (int j = 0; j < eSigList.size(); ++j) {
- PGPSignature emSig = eSigList.get(j);
- if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
- try {
- emSig.init(contentVerifierBuilderProvider, signingPublicKey);
- validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
- }
- }
- }
-
- return validPrimaryKeyBinding;
- }
-
/**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index f64282f5f..1e58c188f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -24,20 +24,14 @@ import android.os.Environment;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.util.Log;
@@ -62,7 +56,6 @@ public class PgpImportExport {
private ProviderHelper mProviderHelper;
public static final int RETURN_OK = 0;
- public static final int RETURN_ERROR = -1;
public static final int RETURN_BAD = -2;
public static final int RETURN_UPDATED = 1;
@@ -100,12 +93,12 @@ public class PgpImportExport {
}
}
- public boolean uploadKeyRingToServer(HkpKeyserver server, PGPPublicKeyRing keyring) {
+ public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
aos = new ArmoredOutputStream(bos);
- aos.write(keyring.getEncoded());
+ keyring.encode(aos);
aos.close();
String armoredKey = bos.toString("UTF-8");
@@ -133,7 +126,7 @@ public class PgpImportExport {
/**
* Imports keys from given data. If keyIds is given only those are imported
*/
- public Bundle importKeyRings(List<ImportKeysListEntry> entries)
+ public Bundle importKeyRings(List<ParcelableKeyRing> entries)
throws PgpGeneralException, PGPException, IOException {
Bundle returnData = new Bundle();
@@ -144,37 +137,26 @@ public class PgpImportExport {
int badKeys = 0;
int position = 0;
- try {
- for (ImportKeysListEntry entry : entries) {
- Object obj = PgpConversionHelper.BytesToPGPKeyRing(entry.getBytes());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing keyring = (PGPKeyRing) obj;
-
- int status = storeKeyRingInCache(keyring);
-
- if (status == RETURN_ERROR) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_saving_keys));
- }
-
- // update the counts to display to the user at the end
- if (status == RETURN_UPDATED) {
- ++oldKeys;
- } else if (status == RETURN_OK) {
- ++newKeys;
- } else if (status == RETURN_BAD) {
- ++badKeys;
- }
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- }
-
- position++;
- updateProgress(position / entries.size() * 100, 100);
+ for (ParcelableKeyRing entry : entries) {
+ try {
+ UncachedKeyRing key = entry.getUncachedKeyRing();
+
+ mProviderHelper.savePublicKeyRing(key);
+ /*switch(status) {
+ case RETURN_UPDATED: oldKeys++; break;
+ case RETURN_OK: newKeys++; break;
+ case RETURN_BAD: badKeys++; break;
+ }*/
+ // TODO proper import feedback
+ newKeys += 1;
+
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "Encountered bad key on import!", e);
+ ++badKeys;
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
+ // update progress
+ position++;
+ updateProgress(position / entries.size() * 100, 100);
}
returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys);
@@ -211,9 +193,11 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- PGPPublicKeyRing publicKeyRing = mProviderHelper.getPGPPublicKeyRing(pubKeyMasterId);
+ WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
+ KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId)
+ );
- publicKeyRing.encode(arOutStream);
+ ring.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: inform user?
@@ -237,7 +221,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId);
+ WrappedSecretKeyRing secretKeyRing =
+ mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
@@ -259,53 +244,4 @@ public class PgpImportExport {
return returnData;
}
- @SuppressWarnings("unchecked")
- public int storeKeyRingInCache(PGPKeyRing keyring) {
- int status = RETURN_ERROR;
- try {
- if (keyring instanceof PGPSecretKeyRing) {
- PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
- boolean save = true;
-
- for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
- secretKeyRing.getSecretKeys())) {
- if (!testSecretKey.isMasterKey()) {
- if (testSecretKey.isPrivateKeyEmpty()) {
- // this is bad, something is very wrong...
- save = false;
- status = RETURN_BAD;
- }
- }
- }
-
- if (save) {
- // TODO: preserve certifications
- // (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
- PGPPublicKeyRing newPubRing = null;
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(
- secretKeyRing.getPublicKeys())) {
- if (newPubRing == null) {
- newPubRing = new PGPPublicKeyRing(key.getEncoded(),
- new JcaKeyFingerprintCalculator());
- }
- newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
- }
- if (newPubRing != null) {
- mProviderHelper.saveKeyRing(newPubRing);
- }
- mProviderHelper.saveKeyRing(secretKeyRing);
- status = RETURN_OK;
- }
- } else if (keyring instanceof PGPPublicKeyRing) {
- PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
- mProviderHelper.saveKeyRing(publicKeyRing);
- status = RETURN_OK;
- }
- } catch (IOException e) {
- status = RETURN_ERROR;
- }
-
- return status;
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index f90250f57..b25c38f1a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -52,14 +52,12 @@ public class PgpKeyHelper {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
+ @Deprecated
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
- public static Date getCreationDate(PGPSecretKey key) {
- return key.getPublicKey().getCreationTime();
- }
-
+ @Deprecated
public static Date getExpiryDate(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
if (key.getValidDays() == 0) {
@@ -73,185 +71,6 @@ public class PgpKeyHelper {
return calendar.getTime();
}
- public static Date getExpiryDate(PGPSecretKey key) {
- return getExpiryDate(key.getPublicKey());
- }
-
- public static boolean isExpired(PGPPublicKey key) {
- Date creationDate = getCreationDate(key);
- Date expiryDate = getExpiryDate(key);
- Date now = new Date();
- if (now.compareTo(creationDate) >= 0
- && (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
- return false;
- }
- return true;
- }
-
- @SuppressWarnings("unchecked")
- public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
- long cnt = 0;
- if (keyRing == null) {
- return null;
- }
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (cnt == num) {
- return key;
- }
- cnt++;
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
- private static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
-
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (isEncryptionKey(key)) {
- encryptKeys.add(key);
- }
- }
-
- return encryptKeys;
- }
-
- @SuppressWarnings("unchecked")
- private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
-
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (isSigningKey(key)) {
- signingKeys.add(key);
- }
- }
-
- return signingKeys;
- }
-
- @SuppressWarnings("unchecked")
- private static Vector<PGPSecretKey> getCertificationKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
-
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (isCertificationKey(key)) {
- signingKeys.add(key);
- }
- }
-
- return signingKeys;
- }
-
- private static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
- Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
- PGPPublicKey masterKey = null;
- for (int i = 0; i < encryptKeys.size(); ++i) {
- PGPPublicKey key = encryptKeys.get(i);
- if (!isExpired(key) && !key.isRevoked()) {
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- private static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
- Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
- PGPSecretKey masterKey = null;
- for (int i = 0; i < signingKeys.size(); ++i) {
- PGPSecretKey key = signingKeys.get(i);
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
- Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
- PGPSecretKey masterKey = null;
- for (int i = 0; i < signingKeys.size(); ++i) {
- PGPSecretKey key = signingKeys.get(i);
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
-
- public static PGPPublicKey getFirstEncryptSubkey(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- Log.e(Constants.TAG, "encryptKeys is null!");
- return null;
- }
- return encryptKeys.get(0);
- }
-
- public static PGPSecretKey getFirstCertificationSubkey(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = getUsableCertificationKeys(keyRing);
- if (signingKeys.size() == 0) {
- return null;
- }
- return signingKeys.get(0);
- }
-
- public static PGPSecretKey getFirstSigningSubkey(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
- if (signingKeys.size() == 0) {
- return null;
- }
- return signingKeys.get(0);
- }
-
- public static int getKeyUsage(PGPSecretKey key) {
- return getKeyUsage(key.getPublicKey());
- }
-
- @SuppressWarnings("unchecked")
- private static int getKeyUsage(PGPPublicKey key) {
- int usage = 0;
- if (key.getVersion() >= 4) {
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
-
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
- if (hashed != null) {
- usage |= hashed.getKeyFlags();
- }
-
- PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
- if (unhashed != null) {
- usage |= unhashed.getKeyFlags();
- }
- }
- }
- return usage;
- }
-
@SuppressWarnings("unchecked")
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
@@ -293,10 +112,6 @@ public class PgpKeyHelper {
return false;
}
- public static boolean isEncryptionKey(PGPSecretKey key) {
- return isEncryptionKey(key.getPublicKey());
- }
-
@SuppressWarnings("unchecked")
public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
@@ -328,10 +143,6 @@ public class PgpKeyHelper {
return false;
}
- public static boolean isSigningKey(PGPSecretKey key) {
- return isSigningKey(key.getPublicKey());
- }
-
@SuppressWarnings("unchecked")
public static boolean isCertificationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
@@ -358,48 +169,6 @@ public class PgpKeyHelper {
return false;
}
- public static boolean isAuthenticationKey(PGPSecretKey key) {
- return isAuthenticationKey(key.getPublicKey());
- }
-
- @SuppressWarnings("unchecked")
- public static boolean isAuthenticationKey(PGPPublicKey key) {
- if (key.getVersion() <= 3) {
- return true;
- }
-
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
-
- if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
- return true;
- }
-
- PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
-
- if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
- return true;
- }
- }
-
- return false;
- }
-
- public static boolean isCertificationKey(PGPSecretKey key) {
- return isCertificationKey(key.getPublicKey());
- }
-
- public static String getAlgorithmInfo(Context context, PGPPublicKey key) {
- return getAlgorithmInfo(context, key.getAlgorithm(), key.getBitStrength());
- }
-
- public static String getAlgorithmInfo(Context context, PGPSecretKey key) {
- return getAlgorithmInfo(context, key.getPublicKey());
- }
-
/**
* TODO: Only used in HkpKeyServer. Get rid of this one!
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 9dd9f660b..44fc4c8c9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -48,8 +48,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes;
@@ -63,6 +63,7 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
@@ -124,7 +125,7 @@ public class PgpKeyOperation {
*/
// TODO: key flags?
- public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
+ public byte[] createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
@@ -188,43 +189,23 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
- sha1Calc, isMasterKey, keyEncryptor);
- }
-
- public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase,
- String newPassphrase)
- throws IOException, PGPException, NoSuchProviderException {
-
- updateProgress(R.string.progress_building_key, 0, 100);
- if (oldPassphrase == null) {
- oldPassphrase = "";
- }
- if (newPassphrase == null) {
- newPassphrase = "";
+ try {
+ return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
+ sha1Calc, isMasterKey, keyEncryptor).getEncoded();
+ } catch(IOException e) {
+ throw new PgpGeneralMsgIdException(R.string.error_encoding);
}
-
- PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
- keyRing,
- new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
- new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
- .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
-
- return newKeyRing;
-
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
- SaveKeyringParcel saveParcel)
+ public Pair<UncachedKeyRing,UncachedKeyRing> buildNewSecretKey(
+ OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = saveParcel.keysUsages.get(0);
boolean canSign;
String mainUserId = saveParcel.userIds.get(0);
- PGPSecretKey masterKey = saveParcel.keys.get(0);
+ PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal();
// this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
@@ -299,7 +280,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100);
- PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
@@ -357,17 +338,19 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
+ return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing));
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR,
- PGPPublicKeyRing pKR,
- SaveKeyringParcel saveParcel)
+ public Pair<UncachedKeyRing, UncachedKeyRing> buildSecretKey(WrappedSecretKeyRing wmKR,
+ WrappedPublicKeyRing wpKR,
+ OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+ PGPSecretKeyRing mKR = wmKR.getRing();
+ PGPPublicKeyRing pKR = wpKR.getRing();
+
updateProgress(R.string.progress_building_key, 0, 100);
- PGPSecretKey masterKey = saveParcel.keys.get(0);
if (saveParcel.oldPassphrase == null) {
saveParcel.oldPassphrase = "";
@@ -399,12 +382,12 @@ public class PgpKeyOperation {
*/
if (saveParcel.deletedKeys != null) {
- for (PGPSecretKey dKey : saveParcel.deletedKeys) {
- mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey);
+ for (UncachedSecretKey dKey : saveParcel.deletedKeys) {
+ mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal());
}
}
- masterKey = mKR.getSecretKey();
+ PGPSecretKey masterKey = mKR.getSecretKey();
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
int usageId = saveParcel.keysUsages.get(0);
@@ -564,7 +547,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
if (saveParcel.moddedKeys[i]) {
- PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2;
@@ -667,7 +650,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
- secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@@ -678,7 +661,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
- publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@@ -686,10 +669,287 @@ public class PgpKeyOperation {
*/
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR);
+ return new Pair<UncachedKeyRing,UncachedKeyRing>(new UncachedKeyRing(pKR),
+ new UncachedKeyRing(mKR));
}
+ public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
+ PGPPublicKeyRing pKR,
+ SaveKeyringParcel saveParcel,
+ String passphrase)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+
+ updateProgress(R.string.progress_building_key, 0, 100);
+
+ // sort these, so we can use binarySearch later on
+ Arrays.sort(saveParcel.revokeSubKeys);
+ Arrays.sort(saveParcel.revokeUserIds);
+
+ /*
+ * What's gonna happen here:
+ *
+ * 1. Unlock private key
+ *
+ * 2. Create new secret key ring
+ *
+ * 3. Copy subkeys
+ * - Generate revocation if requested
+ * - Copy old cert, or generate new if change requested
+ *
+ * 4. Generate and add new subkeys
+ *
+ * 5. Copy user ids
+ * - Generate revocation if requested
+ * - Copy old cert, or generate new if primary user id status changed
+ *
+ * 6. Add new user ids
+ *
+ * 7. Generate PublicKeyRing from SecretKeyRing
+ *
+ * 8. Return pair (PublicKeyRing,SecretKeyRing)
+ *
+ */
+
+ // 1. Unlock private key
+ updateProgress(R.string.progress_building_key, 0, 100);
+
+ PGPPublicKey masterPublicKey = sKR.getPublicKey();
+ PGPPrivateKey masterPrivateKey; {
+ PGPSecretKey masterKey = sKR.getSecretKey();
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
+ }
+
+ // 2. Create new secret key ring
+ updateProgress(R.string.progress_certifying_master_key, 20, 100);
+
+ // Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff
+ // we want to do manually. Instead, we simply use a list of secret keys.
+ ArrayList<PGPSecretKey> secretKeys = new ArrayList<PGPSecretKey>();
+ ArrayList<PGPPublicKey> publicKeys = new ArrayList<PGPPublicKey>();
+
+ // 3. Copy subkeys
+ // - Generate revocation if requested
+ // - Copy old cert, or generate new if change requested
+ for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ PGPPublicKey pKey = sKey.getPublicKey();
+ if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) {
+ // add revocation signature to key, if there is none yet
+ if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) {
+ // generate revocation signature
+ }
+ }
+ if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) {
+ // change subkey flags?
+ SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID());
+ // remove old subkey binding signature(s?)
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) {
+ pKey = PGPPublicKey.removeCertification(pKey, sig);
+ }
+
+ // generate and add new signature
+ PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
+ sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ pKey = PGPPublicKey.addCertification(pKey, sig);
+ }
+ secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey));
+ publicKeys.add(pKey);
+ }
+
+ // 4. Generate and add new subkeys
+ // TODO
+
+ // 5. Copy user ids
+ for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
+ // - Copy old cert, or generate new if primary user id status changed
+ boolean certified = false, revoked = false;
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ masterPublicKey.getSignaturesForID(userId))) {
+ // We know there are only revocation and certification types in here.
+ switch(sig.getSignatureType()) {
+ case PGPSignature.CERTIFICATION_REVOCATION:
+ revoked = true;
+ continue;
+
+ case PGPSignature.DEFAULT_CERTIFICATION:
+ case PGPSignature.NO_CERTIFICATION:
+ case PGPSignature.CASUAL_CERTIFICATION:
+ case PGPSignature.POSITIVE_CERTIFICATION:
+ // Already got one? Remove this one, then.
+ if (certified) {
+ masterPublicKey = PGPPublicKey.removeCertification(
+ masterPublicKey, userId, sig);
+ continue;
+ }
+ boolean primary = userId.equals(saveParcel.changePrimaryUserId);
+ // Generate a new one under certain circumstances
+ if (saveParcel.changePrimaryUserId != null &&
+ sig.getHashedSubPackets().isPrimaryUserID() != primary) {
+ PGPSignature cert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, primary);
+ PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+ certified = true;
+ }
+ }
+ // - Generate revocation if requested
+ if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) {
+ PGPSignature cert = generateRevocationSignature(masterPrivateKey,
+ masterPublicKey, userId);
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+ }
+
+ // 6. Add new user ids
+ for(String userId : saveParcel.addUserIds) {
+ PGPSignature cert = generateUserIdSignature(masterPrivateKey,
+ masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId));
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+
+ // 7. Generate PublicKeyRing from SecretKeyRing
+ updateProgress(R.string.progress_building_master_key, 30, 100);
+ PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys);
+
+ // Copy all non-self uid certificates
+ for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
+ // - Copy old cert, or generate new if primary user id status changed
+ boolean certified = false, revoked = false;
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ masterPublicKey.getSignaturesForID(userId))) {
+ }
+ }
+
+ for (PGPPublicKey newKey : publicKeys) {
+ PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ oldKey.getSignatures())) {
+ }
+ }
+
+ // If requested, set new passphrase
+ if (saveParcel.newPassphrase != null) {
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
+ .get(HashAlgorithmTags.SHA1);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ // Build key encryptor based on new passphrase
+ PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.newPassphrase.toCharArray());
+
+ sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
+ }
+
+ // 8. Return pair (PublicKeyRing,SecretKeyRing)
+
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
+
+ }
+
+ private static PGPSignature generateUserIdSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
+ throws IOException, PGPException, SignatureException {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+ subHashedPacketsGen.setPrimaryUserID(false, primary);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ return sGen.generateCertification(userId, pKey);
+ }
+
+ private static PGPSignature generateRevocationSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
+ throws IOException, PGPException, SignatureException {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
+ return sGen.generateCertification(userId, pKey);
+ }
+
+ private static PGPSignature generateSubkeyBindingSignature(
+ PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
+ PGPSecretKey sKey, PGPPublicKey pKey,
+ int flags, Long expiry, String passphrase)
+ throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
+
+ // date for signing
+ Date todayDate = new Date();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ // If this key can sign, we need a primary key binding signature
+ if ((flags & KeyFlags.SIGN_DATA) != 0) {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ passphrase.toCharArray());
+ PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
+
+ // cross-certify signing keys
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
+ unhashedPacketsGen.setEmbeddedSignature(false, certification);
+ }
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen;
+ {
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ hashedPacketsGen.setKeyFlags(false, flags);
+ }
+
+ if (expiry != null) {
+ Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(pKey.getCreationTime());
+ // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ // here we purposefully ignore partial days in each date - long type has
+ // no fractional part!
+ long numDays = (expiry / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ }
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+
+ return sGen.generateCertification(masterPublicKey, pKey);
+
+ }
+
+
/**
* Certify the given pubkeyid with the given masterkeyid.
*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 665dc82cc..4cb92c368 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -25,25 +25,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -277,20 +266,17 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
- String signingUserId = null;
+ WrappedSecretKey signingKey = null;
if (enableSignature) {
+ WrappedSecretKeyRing signingKeyRing;
try {
- signingKeyRing = mProviderHelper.getPGPSecretKeyRing(mSignatureMasterKeyId);
- signingUserId = (String) mProviderHelper.getUnifiedData(mSignatureMasterKeyId,
- KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
- signingKey = PgpKeyHelper.getFirstSigningSubkey(signingKeyRing);
- if (signingKey == null) {
+ try {
+ signingKey = signingKeyRing.getSigningSubKey();
+ } catch(PgpGeneralException e) {
throw new NoSigningKeyException();
}
@@ -300,10 +286,9 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
- signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
+ try {
+ signingKey.unlock(mSignaturePassphrase);
+ } catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
}
@@ -331,13 +316,12 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
- PGPPublicKeyRing keyRing = mProviderHelper.getPGPPublicKeyRing(id);
- PGPPublicKey key = PgpKeyHelper.getFirstEncryptSubkey(keyRing);
- if (key != null) {
- JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
- new JcePublicKeyKeyEncryptionMethodGenerator(key);
- cPk.addMethod(pubKeyEncryptionGenerator);
- }
+ WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingUri(id));
+ WrappedPublicKey key = keyRing.getEncryptionSubKey();
+ cPk.addMethod(key.getPubKeyEncryptionGenerator());
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "key not found!", e);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
@@ -351,29 +335,18 @@ public class PgpSignEncrypt {
if (enableSignature) {
updateProgress(R.string.progress_preparing_signature, 10, 100);
- // content signer based on signing key algorithm and chosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- int signatureType;
- if (mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption) {
- // for sign-only ascii text
- signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
- } else {
- signatureType = PGPSignature.BINARY_DOCUMENT;
- }
-
- if (mSignatureForceV3) {
- signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
- signatureV3Generator.init(signatureType, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(signatureType, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, signingUserId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
+ try {
+ boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
+ if (mSignatureForceV3) {
+ signatureV3Generator = signingKey.getV3SignatureGenerator(
+ mSignatureHashAlgorithm,cleartext);
+ } else {
+ signatureGenerator = signingKey.getSignatureGenerator(
+ mSignatureHashAlgorithm, cleartext);
+ }
+ } catch (PgpGeneralException e) {
+ // TODO throw correct type of exception (which shouldn't be PGPException)
+ throw new KeyExtractionException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
new file mode 100644
index 000000000..02e5411ca
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -0,0 +1,171 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.S2K;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPUtil;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+/** Wrapper around PGPKeyRing class, to be constructed from bytes.
+ *
+ * This class and its relatives UncachedPublicKey and UncachedSecretKey are
+ * used to move around pgp key rings in non crypto related (UI, mostly) code.
+ * It should be used for simple inspection only until it saved in the database,
+ * all actual crypto operations should work with WrappedKeyRings exclusively.
+ *
+ * This class is also special in that it can hold either the PGPPublicKeyRing
+ * or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
+ * treated equally for most purposes in UI code. It is up to the programmer to
+ * take care of the differences.
+ *
+ * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
+ * @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
+ *
+ */
+public class UncachedKeyRing {
+
+ final PGPKeyRing mRing;
+ final boolean mIsSecret;
+
+ UncachedKeyRing(PGPKeyRing ring) {
+ mRing = ring;
+ mIsSecret = ring instanceof PGPSecretKeyRing;
+ }
+
+ public long getMasterKeyId() {
+ return mRing.getPublicKey().getKeyID();
+ }
+
+ /* TODO don't use this */
+ @Deprecated
+ public PGPKeyRing getRing() {
+ return mRing;
+ }
+
+ public UncachedPublicKey getPublicKey() {
+ return new UncachedPublicKey(mRing.getPublicKey());
+ }
+
+ public Iterator<UncachedPublicKey> getPublicKeys() {
+ final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
+ return new Iterator<UncachedPublicKey>() {
+ public void remove() {
+ it.remove();
+ }
+ public UncachedPublicKey next() {
+ return new UncachedPublicKey(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+
+ /** Returns the dynamic (though final) property if this is a secret keyring or not. */
+ public boolean isSecret() {
+ return mIsSecret;
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return mRing.getEncoded();
+ }
+
+ public byte[] getFingerprint() {
+ return mRing.getPublicKey().getFingerprint();
+ }
+
+ public static UncachedKeyRing decodePublicFromData(byte[] data)
+ throws PgpGeneralException, IOException {
+ UncachedKeyRing ring = decodeFromData(data);
+ if(ring.isSecret()) {
+ throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
+ }
+ return ring;
+ }
+
+ public static UncachedKeyRing decodeFromData(byte[] data)
+ throws PgpGeneralException, IOException {
+ BufferedInputStream bufferedInput =
+ new BufferedInputStream(new ByteArrayInputStream(data));
+ if (bufferedInput.available() > 0) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+
+ // get first object in block
+ Object obj;
+ if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
+ return new UncachedKeyRing((PGPKeyRing) obj);
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
+ }
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
+ }
+ }
+
+ public static List<UncachedKeyRing> fromStream(InputStream stream)
+ throws PgpGeneralException, IOException {
+
+ PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+
+ List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
+
+ // go through all objects in this block
+ Object obj;
+ while ((obj = objectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+
+ if (obj instanceof PGPKeyRing) {
+ result.add(new UncachedKeyRing((PGPKeyRing) obj));
+ } else {
+ Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
+ }
+ }
+ return result;
+ }
+
+ public void encodeArmored(OutputStream out, String version) throws IOException {
+ ArmoredOutputStream aos = new ArmoredOutputStream(out);
+ aos.setHeader("Version", version);
+ aos.write(mRing.getEncoded());
+ aos.close();
+ }
+
+ public ArrayList<Long> getAvailableSubkeys() {
+ if(!isSecret()) {
+ throw new RuntimeException("Tried to find available subkeys from non-secret keys. " +
+ "This is a programming error and should never happen!");
+ }
+
+ ArrayList<Long> result = new ArrayList<Long>();
+ // then, mark exactly the keys we have available
+ for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
+ ((PGPSecretKeyRing) mRing).getSecretKeys())) {
+ S2K s2k = sub.getS2K();
+ // Set to 1, except if the encryption type is GNU_DUMMY_S2K
+ if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
+ result.add(sub.getKeyID());
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
new file mode 100644
index 000000000..e3db03bf6
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -0,0 +1,197 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+
+public class UncachedPublicKey {
+ protected final PGPPublicKey mPublicKey;
+ private Integer mCacheUsage = null;
+
+ public UncachedPublicKey(PGPPublicKey key) {
+ mPublicKey = key;
+ }
+
+ public long getKeyId() {
+ return mPublicKey.getKeyID();
+ }
+
+ /** The revocation signature is NOT checked here, so this may be false! */
+ public boolean maybeRevoked() {
+ return mPublicKey.isRevoked();
+ }
+
+ public Date getCreationTime() {
+ return mPublicKey.getCreationTime();
+ }
+
+ public Date getExpiryTime() {
+ Date creationDate = getCreationTime();
+ if (mPublicKey.getValidDays() == 0) {
+ // no expiry
+ return null;
+ }
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTime(creationDate);
+ calendar.add(Calendar.DATE, mPublicKey.getValidDays());
+
+ return calendar.getTime();
+ }
+
+ public boolean isExpired() {
+ Date creationDate = mPublicKey.getCreationTime();
+ Date expiryDate = mPublicKey.getValidSeconds() > 0
+ ? new Date(creationDate.getTime() + mPublicKey.getValidSeconds() * 1000) : null;
+
+ Date now = new Date();
+ return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
+ }
+
+ public boolean isMasterKey() {
+ return mPublicKey.isMasterKey();
+ }
+
+ public int getAlgorithm() {
+ return mPublicKey.getAlgorithm();
+ }
+
+ public int getBitStrength() {
+ return mPublicKey.getBitStrength();
+ }
+
+ public String getPrimaryUserId() {
+ for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
+ if (sig.getHashedSubPackets() != null
+ && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
+ try {
+ // make sure it's actually valid
+ sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
+ if (sig.verifyCertification(userId, mPublicKey)) {
+ return userId;
+ }
+ } catch (Exception e) {
+ // nothing bad happens, the key is just not considered the primary key id
+ }
+ }
+
+ }
+ }
+ return null;
+ }
+
+ public ArrayList<String> getUnorderedUserIds() {
+ ArrayList<String> userIds = new ArrayList<String>();
+ for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ userIds.add(userId);
+ }
+ return userIds;
+ }
+
+ public boolean isElGamalEncrypt() {
+ return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
+ }
+
+ public boolean isDSA() {
+ return getAlgorithm() == PGPPublicKey.DSA;
+ }
+
+ @SuppressWarnings("unchecked")
+ public int getKeyUsage() {
+ if(mCacheUsage == null) {
+ mCacheUsage = 0;
+ if (mPublicKey.getVersion() >= 4) {
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
+ if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
+ continue;
+ }
+
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ if (hashed != null) {
+ mCacheUsage |= hashed.getKeyFlags();
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+ if (unhashed != null) {
+ mCacheUsage |= unhashed.getKeyFlags();
+ }
+ }
+ }
+ }
+ return mCacheUsage;
+ }
+
+ public boolean canAuthenticate() {
+ return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
+ }
+
+ public boolean canCertify() {
+ return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
+ }
+
+ public boolean canEncrypt() {
+ if (!mPublicKey.isEncryptionKey()) {
+ return false;
+ }
+
+ // special cases
+ if (mPublicKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
+ return true;
+ }
+
+ if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
+ return true;
+ }
+
+ return mPublicKey.getVersion() <= 3 ||
+ (getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
+
+ }
+
+ public boolean canSign() {
+ // special case
+ if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
+ return true;
+ }
+
+ return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
+ }
+
+ public byte[] getFingerprint() {
+ return mPublicKey.getFingerprint();
+ }
+
+ // TODO This method should have package visibility - no access outside the pgp package!
+ // (It's still used in ProviderHelper at this point)
+ public PGPPublicKey getPublicKey() {
+ return mPublicKey;
+ }
+
+ public Iterator<WrappedSignature> getSignaturesForId(String userId) {
+ final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
+ return new Iterator<WrappedSignature>() {
+ public void remove() {
+ it.remove();
+ }
+ public WrappedSignature next() {
+ return new WrappedSignature(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
new file mode 100644
index 000000000..0e14a7fd3
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
@@ -0,0 +1,33 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPSecretKey;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class UncachedSecretKey extends UncachedPublicKey {
+
+ public static final int CERTIFY_OTHER = KeyFlags.CERTIFY_OTHER;
+ public static final int SIGN_DATA = KeyFlags.SIGN_DATA;
+ public static final int ENCRYPT_COMMS = KeyFlags.ENCRYPT_COMMS;
+ public static final int ENCRYPT_STORAGE = KeyFlags.ENCRYPT_STORAGE;
+ public static final int AUTHENTICATION = KeyFlags.AUTHENTICATION;
+
+ final PGPSecretKey mSecretKey;
+
+ public UncachedSecretKey(PGPSecretKey secretKey) {
+ super(secretKey.getPublicKey());
+ mSecretKey = secretKey;
+ }
+
+ @Deprecated
+ public PGPSecretKey getSecretKeyExternal() {
+ return mSecretKey;
+ }
+
+ public void encodeSecretKey(OutputStream os) throws IOException {
+ mSecretKey.encode(os);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
new file mode 100644
index 000000000..2b6049894
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
@@ -0,0 +1,97 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** A generic wrapped PGPKeyRing object.
+ *
+ * This class provides implementations for all basic getters which both
+ * PublicKeyRing and SecretKeyRing have in common. To make the wrapped keyring
+ * class typesafe in implementing subclasses, the field is stored in the
+ * implementing class, providing properly typed access through the getRing
+ * getter method.
+ *
+ */
+public abstract class WrappedKeyRing extends KeyRing {
+
+ private final boolean mHasAnySecret;
+ private final int mVerified;
+
+ WrappedKeyRing(boolean hasAnySecret, int verified) {
+ mHasAnySecret = hasAnySecret;
+ mVerified = verified;
+ }
+
+ public long getMasterKeyId() {
+ return getRing().getPublicKey().getKeyID();
+ }
+
+ public boolean hasAnySecret() {
+ return mHasAnySecret;
+ }
+
+ public int getVerified() {
+ return mVerified;
+ }
+
+ public String getPrimaryUserId() throws PgpGeneralException {
+ return (String) getRing().getPublicKey().getUserIDs().next();
+ };
+
+ public boolean isRevoked() throws PgpGeneralException {
+ // Is the master key revoked?
+ return getRing().getPublicKey().isRevoked();
+ }
+
+ public boolean canCertify() throws PgpGeneralException {
+ return getRing().getPublicKey().isEncryptionKey();
+ }
+
+ public long getEncryptId() throws PgpGeneralException {
+ for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
+ if(PgpKeyHelper.isEncryptionKey(key)) {
+ return key.getKeyID();
+ }
+ }
+ throw new PgpGeneralException("No valid encryption key found!");
+ }
+
+ public boolean hasEncrypt() throws PgpGeneralException {
+ try {
+ getEncryptId();
+ return true;
+ } catch(PgpGeneralException e) {
+ return false;
+ }
+ }
+
+ public long getSignId() throws PgpGeneralException {
+ for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
+ if(PgpKeyHelper.isSigningKey(key)) {
+ return key.getKeyID();
+ }
+ }
+ throw new PgpGeneralException("No valid signing key found!");
+ }
+
+ public boolean hasSign() throws PgpGeneralException {
+ try {
+ getSignId();
+ return true;
+ } catch (PgpGeneralException e) {
+ return false;
+ }
+ }
+
+ public void encode(OutputStream stream) throws IOException {
+ getRing().encode(stream);
+ }
+
+ abstract PGPKeyRing getRing();
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
new file mode 100644
index 000000000..69a4fbdee
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
@@ -0,0 +1,39 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+/** Wrapper for a PGPPublicKey.
+ *
+ * The methods implemented in this class are a thin layer over
+ * UncachedPublicKey. The difference between the two classes is that objects of
+ * this class can only be obtained from a WrappedKeyRing, and that it stores a
+ * back reference to its parent as well. A method which works with
+ * WrappedPublicKey is therefore guaranteed to work on a KeyRing which is
+ * stored in the database.
+ *
+ */
+public class WrappedPublicKey extends UncachedPublicKey {
+
+ // this is the parent key ring
+ final KeyRing mRing;
+
+ WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
+ super(key);
+ mRing = ring;
+ }
+
+ public IterableIterator<String> getUserIds() {
+ return new IterableIterator<String>(mPublicKey.getUserIDs());
+ }
+
+ public KeyRing getKeyRing() {
+ return mRing;
+ }
+
+ JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
+ return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
new file mode 100644
index 000000000..99dc99436
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
@@ -0,0 +1,192 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class WrappedPublicKeyRing extends WrappedKeyRing {
+
+ private PGPPublicKeyRing mRing;
+ private final byte[] mPubKey;
+
+ public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
+ super(hasAnySecret, verified);
+ mPubKey = blob;
+ }
+
+ PGPPublicKeyRing getRing() {
+ if(mRing == null) {
+ PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
+ PGPKeyRing keyRing = null;
+ try {
+ if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ }
+
+ mRing = (PGPPublicKeyRing) keyRing;
+ }
+ return mRing;
+ }
+
+ public void encode(ArmoredOutputStream stream) throws IOException {
+ getRing().encode(stream);
+ }
+
+ public WrappedPublicKey getSubkey() {
+ return new WrappedPublicKey(this, getRing().getPublicKey());
+ }
+
+ public WrappedPublicKey getSubkey(long id) {
+ return new WrappedPublicKey(this, getRing().getPublicKey(id));
+ }
+
+ /** Getter that returns the subkey that should be used for signing. */
+ WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
+ PGPPublicKey key = getRing().getPublicKey(getEncryptId());
+ if(key != null) {
+ WrappedPublicKey cKey = new WrappedPublicKey(this, key);
+ if(!cKey.canEncrypt()) {
+ throw new PgpGeneralException("key error");
+ }
+ return cKey;
+ }
+ // TODO handle with proper exception
+ throw new PgpGeneralException("no encryption key available");
+ }
+
+ public boolean verifySubkeyBinding(WrappedPublicKey cachedSubkey) {
+ boolean validSubkeyBinding = false;
+ boolean validTempSubkeyBinding = false;
+ boolean validPrimaryKeyBinding = false;
+
+ PGPPublicKey masterKey = getRing().getPublicKey();
+ PGPPublicKey subKey = cachedSubkey.getPublicKey();
+
+ // Is this the master key? Match automatically, then.
+ if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {
+ return true;
+ }
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ Iterator<PGPSignature> itr = subKey.getSignatures();
+
+ while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
+ //gpg has an invalid subkey binding error on key import I think, but doesn't shout
+ //about keys without subkey signing. Can't get it to import a slightly broken one
+ //either, so we will err on bad subkey binding here.
+ PGPSignature sig = itr.next();
+ if (sig.getKeyID() == masterKey.getKeyID() &&
+ sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
+ //check and if ok, check primary key binding.
+ try {
+ sig.init(contentVerifierBuilderProvider, masterKey);
+ validTempSubkeyBinding = sig.verifyCertification(masterKey, subKey);
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+
+ if (validTempSubkeyBinding) {
+ validSubkeyBinding = true;
+ }
+ if (validTempSubkeyBinding) {
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
+ masterKey, subKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
+ masterKey, subKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ }
+ }
+ }
+ return validSubkeyBinding && validPrimaryKeyBinding;
+
+ }
+
+ static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
+ PGPPublicKey masterPublicKey,
+ PGPPublicKey signingPublicKey) {
+ boolean validPrimaryKeyBinding = false;
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureList eSigList;
+
+ if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
+ try {
+ eSigList = pkts.getEmbeddedSignatures();
+ } catch (IOException e) {
+ return false;
+ } catch (PGPException e) {
+ return false;
+ }
+ for (int j = 0; j < eSigList.size(); ++j) {
+ PGPSignature emSig = eSigList.get(j);
+ if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
+ try {
+ emSig.init(contentVerifierBuilderProvider, signingPublicKey);
+ validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ return validPrimaryKeyBinding;
+ }
+
+ public IterableIterator<WrappedPublicKey> iterator() {
+ final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
+ return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public WrappedPublicKey next() {
+ return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
+ }
+
+ @Override
+ public void remove() {
+ it.remove();
+ }
+ });
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
new file mode 100644
index 000000000..ef8044a9b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
@@ -0,0 +1,200 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.PGPV3SignatureGenerator;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+import java.util.List;
+
+/** Wrapper for a PGPSecretKey.
+ *
+ * This object can only be obtained from a WrappedSecretKeyRing, and stores a
+ * back reference to its parent.
+ *
+ * This class represents known secret keys which are stored in the database.
+ * All "crypto operations using a known secret key" should be implemented in
+ * this class, to ensure on type level that these operations are performed on
+ * properly imported secret keys only.
+ *
+ */
+public class WrappedSecretKey extends WrappedPublicKey {
+
+ private final PGPSecretKey mSecretKey;
+ private PGPPrivateKey mPrivateKey = null;
+
+ WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
+ super(ring, key.getPublicKey());
+ mSecretKey = key;
+ }
+
+ public WrappedSecretKeyRing getRing() {
+ return (WrappedSecretKeyRing) mRing;
+ }
+
+ /** Returns the wrapped PGPSecretKeyRing.
+ * This function is for compatibility only, should not be used anymore and will be removed
+ */
+ @Deprecated
+ public PGPSecretKey getKeyExternal() {
+ return mSecretKey;
+ }
+
+ public boolean unlock(String passphrase) throws PgpGeneralException {
+ try {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
+ } catch (PGPException e) {
+ return false;
+ }
+ if(mPrivateKey == null) {
+ throw new PgpGeneralException("error extracting key");
+ }
+ return true;
+ }
+
+ public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext)
+ throws PgpGeneralException {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ int signatureType;
+ if (cleartext) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ try {
+ PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(signatureType, mPrivateKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, mRing.getPrimaryUserId());
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ return signatureGenerator;
+ } catch(PGPException e) {
+ throw new PgpGeneralException("Error initializing signature!", e);
+ }
+ }
+
+ public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext)
+ throws PgpGeneralException {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ int signatureType;
+ if (cleartext) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ try {
+ PGPV3SignatureGenerator signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
+ signatureV3Generator.init(signatureType, mPrivateKey);
+ return signatureV3Generator;
+ } catch(PGPException e) {
+ throw new PgpGeneralException("Error initializing signature!", e);
+ }
+ }
+
+ public PublicKeyDataDecryptorFactory getDecryptorFactory() {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+ return new JcePublicKeyDataDecryptorFactoryBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
+ }
+
+ /**
+ * Certify the given pubkeyid with the given masterkeyid.
+ *
+ * @param publicKeyRing Keyring to add certification to.
+ * @param userIds User IDs to certify, must not be null or empty
+ * @return A keyring with added certifications
+ */
+ public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds)
+ throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
+ PGPException, SignatureException {
+
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator;
+ {
+ // TODO: SHA256 fixed?
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
+ }
+
+ { // supply signatureGenerator with a SubpacketVector
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ signatureGenerator.setHashedSubpackets(packetVector);
+ }
+
+ // get the master subkey (which we certify for)
+ PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
+
+ // fetch public key ring, add the certification and return it
+ for (String userId : new IterableIterator<String>(userIds.iterator())) {
+ PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
+ publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
+ }
+
+ PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
+
+ return new UncachedKeyRing(ring);
+ }
+
+ static class PrivateKeyNotUnlockedException extends RuntimeException {
+ // this exception is a programming error which happens when an operation which requires
+ // the private key is called without a previous call to unlock()
+ }
+
+ public UncachedSecretKey getUncached() {
+ return new UncachedSecretKey(mSecretKey);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
new file mode 100644
index 000000000..91d4286f4
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
@@ -0,0 +1,141 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.security.NoSuchProviderException;
+import java.util.Iterator;
+
+public class WrappedSecretKeyRing extends WrappedKeyRing {
+
+ private PGPSecretKeyRing mRing;
+
+ public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
+ {
+ super(isRevoked, verified);
+ PGPObjectFactory factory = new PGPObjectFactory(blob);
+ PGPKeyRing keyRing = null;
+ try {
+ if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ }
+
+ mRing = (PGPSecretKeyRing) keyRing;
+ }
+
+ PGPSecretKeyRing getRing() {
+ return mRing;
+ }
+
+ public WrappedSecretKey getSubKey() {
+ return new WrappedSecretKey(this, mRing.getSecretKey());
+ }
+
+ public WrappedSecretKey getSubKey(long id) {
+ return new WrappedSecretKey(this, mRing.getSecretKey(id));
+ }
+
+ /** Getter that returns the subkey that should be used for signing. */
+ WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
+ PGPSecretKey key = mRing.getSecretKey(getSignId());
+ if(key != null) {
+ WrappedSecretKey cKey = new WrappedSecretKey(this, key);
+ if(!cKey.canSign()) {
+ throw new PgpGeneralException("key error");
+ }
+ return cKey;
+ }
+ // TODO handle with proper exception
+ throw new PgpGeneralException("no signing key available");
+ }
+
+ public boolean hasPassphrase() {
+ PGPSecretKey secretKey = null;
+ boolean foundValidKey = false;
+ for (Iterator keys = mRing.getSecretKeys(); keys.hasNext(); ) {
+ secretKey = (PGPSecretKey) keys.next();
+ if (!secretKey.isPrivateKeyEmpty()) {
+ foundValidKey = true;
+ break;
+ }
+ }
+ if(!foundValidKey) {
+ return false;
+ }
+
+ try {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider("SC").build("".toCharArray());
+ PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
+ return testKey == null;
+ } catch(PGPException e) {
+ // this means the crc check failed -> passphrase required
+ return true;
+ }
+ }
+
+ public UncachedKeyRing changeSecretKeyPassphrase(String oldPassphrase,
+ String newPassphrase)
+ throws IOException, PGPException, NoSuchProviderException {
+
+ if (oldPassphrase == null) {
+ oldPassphrase = "";
+ }
+ if (newPassphrase == null) {
+ newPassphrase = "";
+ }
+
+ PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
+ mRing,
+ new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
+ new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey()
+ .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
+
+ return new UncachedKeyRing(newKeyRing);
+
+ }
+
+ public IterableIterator<WrappedSecretKey> iterator() {
+ final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
+ return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public WrappedSecretKey next() {
+ return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
+ }
+
+ @Override
+ public void remove() {
+ it.remove();
+ }
+ });
+ }
+
+ public UncachedKeyRing getUncached() {
+ return new UncachedKeyRing(mRing);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
new file mode 100644
index 000000000..1b7a5e8ba
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -0,0 +1,161 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.SignatureSubpacket;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.sig.RevocationReason;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.security.SignatureException;
+import java.util.Date;
+
+/** OpenKeychain wrapper around PGPSignature objects.
+ *
+ * This is a mostly simple wrapper around a single bouncycastle PGPSignature
+ * object. It exposes high level getters for all relevant information, methods
+ * for verification of various signatures (uid binding, subkey binding, generic
+ * bytes), and a static method for construction from bytes.
+ *
+ */
+public class WrappedSignature {
+
+ public static final int DEFAULT_CERTIFICATION = PGPSignature.DEFAULT_CERTIFICATION;
+ public static final int NO_CERTIFICATION = PGPSignature.NO_CERTIFICATION;
+ public static final int CASUAL_CERTIFICATION = PGPSignature.CASUAL_CERTIFICATION;
+ public static final int POSITIVE_CERTIFICATION = PGPSignature.POSITIVE_CERTIFICATION;
+ public static final int CERTIFICATION_REVOCATION = PGPSignature.CERTIFICATION_REVOCATION;
+
+ final PGPSignature mSig;
+
+ protected WrappedSignature(PGPSignature sig) {
+ mSig = sig;
+ }
+
+ public long getKeyId() {
+ return mSig.getKeyID();
+ }
+
+ public int getSignatureType() {
+ return mSig.getSignatureType();
+ }
+
+ public int getKeyAlgorithm() {
+ return mSig.getKeyAlgorithm();
+ }
+
+ public Date getCreationTime() {
+ return mSig.getCreationTime();
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return mSig.getEncoded();
+ }
+
+ public boolean isRevocation() {
+ return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
+ }
+
+ public boolean isPrimaryUserId() {
+ return mSig.getHashedSubPackets().isPrimaryUserID();
+ }
+
+ public String getRevocationReason() throws PgpGeneralException {
+ if(!isRevocation()) {
+ throw new PgpGeneralException("Not a revocation signature.");
+ }
+ SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
+ SignatureSubpacketTags.REVOCATION_REASON);
+ // For some reason, this is missing in SignatureSubpacketInputStream:146
+ if (!(p instanceof RevocationReason)) {
+ p = new RevocationReason(false, p.getData());
+ }
+ return ((RevocationReason) p).getRevocationDescription();
+ }
+
+ public void init(WrappedPublicKey key) throws PgpGeneralException {
+ init(key.getPublicKey());
+ }
+
+ public void init(UncachedPublicKey key) throws PgpGeneralException {
+ init(key.getPublicKey());
+ }
+
+ protected void init(PGPPublicKey key) throws PgpGeneralException {
+ try {
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ mSig.init(contentVerifierBuilderProvider, key);
+ } catch(PGPException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public void update(byte[] data, int offset, int length) throws PgpGeneralException {
+ try {
+ mSig.update(data, offset, length);
+ } catch(SignatureException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public void update(byte data) throws PgpGeneralException {
+ try {
+ mSig.update(data);
+ } catch(SignatureException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean verify() throws PgpGeneralException {
+ try {
+ return mSig.verify();
+ } catch(SignatureException e) {
+ throw new PgpGeneralException(e);
+ } catch(PGPException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
+ try {
+ return mSig.verifyCertification(uid, key);
+ } catch (SignatureException e) {
+ throw new PgpGeneralException("Error!", e);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("Error!", e);
+ }
+ }
+
+ public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
+ return verifySignature(key.getPublicKey(), uid);
+ }
+ public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
+ return verifySignature(key.getPublicKey(), uid);
+ }
+
+ public static WrappedSignature fromBytes(byte[] data) {
+ PGPObjectFactory factory = new PGPObjectFactory(data);
+ PGPSignatureList signatures = null;
+ try {
+ if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
+ Log.e(Constants.TAG, "No signatures given!");
+ return null;
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
+ return null;
+ }
+
+ return new WrappedSignature(signatures.get(0));
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
index 37d21eea4..f37a61852 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
@@ -27,4 +27,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message, Throwable cause) {
super(message, cause);
}
+ public PgpGeneralException(Throwable cause) {
+ super(cause);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
new file mode 100644
index 000000000..48d40430a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -0,0 +1,161 @@
+package org.sufficientlysecure.keychain.provider;
+
+import android.net.Uri;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.Log;
+
+/** This implementation of KeyRing provides a cached view of PublicKeyRing
+ * objects based on database queries exclusively.
+ *
+ * This class should be used where only few points of data but no actual
+ * cryptographic operations are required about a PublicKeyRing which is already
+ * in the database. This happens commonly in UI code, where parsing of a PGP
+ * key for examination would be a very expensive operation.
+ *
+ * Each getter method is implemented using a more or less expensive database
+ * query, while object construction is (almost) free. A common pattern is
+ * mProviderHelper.getCachedKeyRing(uri).getterMethod()
+ *
+ * TODO Ensure that the values returned here always match the ones returned by
+ * the parsed KeyRing!
+ *
+ */
+public class CachedPublicKeyRing extends KeyRing {
+
+ final ProviderHelper mProviderHelper;
+ final Uri mUri;
+
+ public CachedPublicKeyRing(ProviderHelper providerHelper, Uri uri) {
+ mProviderHelper = providerHelper;
+ mUri = uri;
+ }
+
+ public long getMasterKeyId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID, ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch (ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ /**
+ * Find the master key id related to a given query. The id will either be extracted from the
+ * query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
+ */
+ public long extractOrGetMasterKeyId() throws PgpGeneralException {
+ // try extracting from the uri first
+ String firstSegment = mUri.getPathSegments().get(1);
+ if (!firstSegment.equals("find")) try {
+ return Long.parseLong(firstSegment);
+ } catch (NumberFormatException e) {
+ // didn't work? oh well.
+ Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
+ }
+ return getMasterKeyId();
+ }
+
+ public String getPrimaryUserId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_STRING);
+ return (String) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean isRevoked() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean canCertify() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public long getEncryptId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean hasEncrypt() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public long getSignId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean hasSign() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public int getVerified() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Integer) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean hasAnySecret() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index a4fa3dac9..483f762f7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -110,6 +110,8 @@ public class KeychainContract {
public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign";
+ public static final String PUBKEY_DATA = "pubkey_data";
+ public static final String PRIVKEY_DATA = "privkey_data";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@@ -123,6 +125,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
+ public static Uri buildGenericKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
+ }
+
public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
@@ -131,20 +137,24 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build();
}
- public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
+ public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
+ .appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingUri(Uri uri) {
- return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1))
+ .appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
- return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build();
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
+ .appendPath(PATH_BY_EMAIL).appendPath(email).build();
}
- public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) {
- return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build();
+ public static Uri buildUnifiedKeyRingsFindBySubkeyUri(long subkey) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
+ .appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 68726d3e0..ceaa93f9b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -23,11 +23,9 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
@@ -256,6 +254,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}.getReadableDatabase();
Cursor cursor = null;
+ ProviderHelper providerHelper = new ProviderHelper(context);
+
try {
// we insert in two steps: first, all public keys that have secret keys
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
@@ -266,14 +266,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0);
- PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
- ProviderHelper providerHelper = new ProviderHelper(context);
- if (ring instanceof PGPPublicKeyRing)
- providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
- else if (ring instanceof PGPSecretKeyRing)
- providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
- else {
- Log.e(Constants.TAG, "Unknown blob data type!");
+ try {
+ UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
+ providerHelper.savePublicKeyRing(ring);
+ } catch(PgpGeneralException e) {
+ Log.e(Constants.TAG, "Error decoding keyring blob!");
}
}
}
@@ -293,14 +290,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0);
- PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
- ProviderHelper providerHelper = new ProviderHelper(context);
- if (ring instanceof PGPPublicKeyRing) {
- providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
- } else if (ring instanceof PGPSecretKeyRing) {
- providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
- } else {
- Log.e(Constants.TAG, "Unknown blob data type!");
+ try {
+ UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
+ providerHelper.savePublicKeyRing(ring);
+ } catch(PgpGeneralException e) {
+ Log.e(Constants.TAG, "Error decoding keyring blob!");
}
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index ec7bf58d9..b651069e9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
public class KeychainProvider extends ContentProvider {
@@ -242,45 +243,39 @@ public class KeychainProvider extends ContentProvider {
HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
- projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID);
- projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);
+ projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE);
projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
- projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY);
- projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
- projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN);
+ projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);
+ projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT);
+ projectionMap.put(KeyRings.CAN_SIGN, Tables.KEYS + "." + Keys.CAN_SIGN);
projectionMap.put(KeyRings.CREATION, Tables.KEYS + "." + Keys.CREATION);
- projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY);
- projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM);
- projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
+ projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
+ projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
+ projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
- projectionMap.put(KeyRings.HAS_SECRET, KeyRings.HAS_SECRET);
+ projectionMap.put(KeyRings.PUBKEY_DATA,
+ Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
+ + " AS " + KeyRings.PUBKEY_DATA);
+ projectionMap.put(KeyRings.PRIVKEY_DATA,
+ Tables.KEY_RINGS_SECRET + "." + KeyRingData.KEY_RING_DATA
+ + " AS " + KeyRings.PRIVKEY_DATA);
+ projectionMap.put(KeyRings.HAS_SECRET, Tables.KEYS + "." + KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.HAS_ANY_SECRET,
"(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET
+ " WHERE " + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ ")) AS " + KeyRings.HAS_ANY_SECRET);
projectionMap.put(KeyRings.HAS_ENCRYPT,
- "(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k"
- +" WHERE k." + Keys.MASTER_KEY_ID
- + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
- + " AND k." + Keys.IS_REVOKED + " = 0"
- + " AND k." + Keys.CAN_ENCRYPT + " = 1"
- + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
- + " >= " + new Date().getTime() / 1000 + " )"
- + ")) AS " + KeyRings.HAS_ENCRYPT);
+ "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN,
- "(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k"
- +" WHERE k." + Keys.MASTER_KEY_ID
- + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
- + " AND k." + Keys.IS_REVOKED + " = 0"
- + " AND k." + Keys.HAS_SECRET + " = 1"
- + " AND k." + Keys.CAN_SIGN + " = 1"
- + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
- + " >= " + new Date().getTime() / 1000 + " )"
- + ")) AS " + KeyRings.HAS_SIGN);
+ "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
qb.setProjectionMap(projectionMap);
+ // Need this as list so we can search in it
+ List<String> plist = Arrays.asList(projection);
+
qb.setTables(
Tables.KEYS
+ " INNER JOIN " + Tables.USER_IDS + " ON ("
@@ -295,6 +290,37 @@ public class KeychainProvider extends ContentProvider {
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED
+ " = " + Certs.VERIFIED_SECRET
+ ")"
+ // fairly expensive joins following, only do when requested
+ + (plist.contains(KeyRings.PUBKEY_DATA) ?
+ " INNER JOIN " + Tables.KEY_RINGS_PUBLIC + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID
+ + ")" : "")
+ + (plist.contains(KeyRings.PRIVKEY_DATA) ?
+ " LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ + ")" : "")
+ + (plist.contains(KeyRings.HAS_ENCRYPT) ?
+ " LEFT JOIN " + Tables.KEYS + " AS kE ON ("
+ +"kE." + Keys.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND kE." + Keys.IS_REVOKED + " = 0"
+ + " AND kE." + Keys.CAN_ENCRYPT + " = 1"
+ + " AND ( kE." + Keys.EXPIRY + " IS NULL OR kE." + Keys.EXPIRY
+ + " >= " + new Date().getTime() / 1000 + " )"
+ + ")" : "")
+ + (plist.contains(KeyRings.HAS_SIGN) ?
+ " LEFT JOIN " + Tables.KEYS + " AS kS ON ("
+ +"kS." + Keys.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND kS." + Keys.IS_REVOKED + " = 0"
+ + " AND kS." + Keys.CAN_SIGN + " = 1"
+ + " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY
+ + " >= " + new Date().getTime() / 1000 + " )"
+ + ")" : "")
);
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates
@@ -595,7 +621,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_CERTS:
// we replace here, keeping only the latest signature
- // TODO this would be better handled in saveKeyRing directly!
+ // TODO this would be better handled in savePublicKeyRing directly!
db.replaceOrThrow(Tables.CERTS, null, values);
keyId = values.getAsLong(Certs.MASTER_KEY_ID);
break;
@@ -618,7 +644,7 @@ public class KeychainProvider extends ContentProvider {
}
if(keyId != null) {
- uri = KeyRings.buildGenericKeyRingUri(keyId.toString());
+ uri = KeyRings.buildGenericKeyRingUri(keyId);
rowUri = uri;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index ab00db13a..043c40b25 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -23,26 +23,20 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.S2K;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@@ -56,14 +50,12 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
public class ProviderHelper {
@@ -141,47 +133,29 @@ public class ProviderHelper {
public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types)
throws NotFoundException {
- return getGenericData(KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types);
+ return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
}
- /**
- * Find the master key id related to a given query. The id will either be extracted from the
- * query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
- */
- public long extractOrGetMasterKeyId(Uri queryUri)
- throws NotFoundException {
- // try extracting from the uri first
- String firstSegment = queryUri.getPathSegments().get(1);
- if (!firstSegment.equals("find")) try {
- return Long.parseLong(firstSegment);
- } catch (NumberFormatException e) {
- // didn't work? oh well.
- Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
- }
- return getMasterKeyId(queryUri);
- }
-
- public long getMasterKeyId(Uri queryUri) throws NotFoundException {
- Object data = getGenericData(queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
- if (data != null) {
- return (Long) data;
- } else {
- throw new NotFoundException();
- }
- }
-
- public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
+ private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
null, null, null);
- LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
+ LongSparseArray<UncachedPublicKey> result =
+ new LongSparseArray<UncachedPublicKey>(cursor.getCount());
try {
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
byte[] data = cursor.getBlob(1);
if (data != null) {
- result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
+ try {
+ result.put(masterKeyId,
+ UncachedKeyRing.decodePublicFromData(data).getPublicKey());
+ } catch(PgpGeneralException e) {
+ Log.e(Constants.TAG, "Error parsing keyring, skipping.");
+ } catch(IOException e) {
+ Log.e(Constants.TAG, "IO error, skipping keyring");
+ }
}
} while (cursor.moveToNext());
} finally {
@@ -193,57 +167,74 @@ public class ProviderHelper {
return result;
}
- public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
- LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri);
- if (result.size() == 0) {
- throw new NotFoundException("PGPKeyRing object not found!");
- } else {
- return result.valueAt(0);
- }
+ public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) {
+ return new CachedPublicKeyRing(this, queryUri);
}
- public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId)
- throws NotFoundException {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
- long masterKeyId = getMasterKeyId(uri);
- return getPGPPublicKeyRing(masterKeyId);
+ public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException {
+ return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
}
- public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId)
- throws NotFoundException {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
- long masterKeyId = getMasterKeyId(uri);
- return getPGPSecretKeyRing(masterKeyId);
+ public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException {
+ return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false);
}
- /**
- * Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
- */
- public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException {
- Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
- return (PGPPublicKeyRing) getPGPKeyRing(queryUri);
+ public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException {
+ return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
}
- /**
- * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
- */
- public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException {
- Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
- return (PGPSecretKeyRing) getPGPKeyRing(queryUri);
+ public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException {
+ return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true);
+ }
+
+ private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
+ Cursor cursor = mContentResolver.query(queryUri,
+ new String[]{
+ // we pick from cache only information that is not easily available from keyrings
+ KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
+ // and of course, ring data
+ secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
+ }, null, null, null
+ );
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+
+ boolean hasAnySecret = cursor.getInt(0) > 0;
+ int verified = cursor.getInt(1);
+ byte[] blob = cursor.getBlob(2);
+ if(secret &! hasAnySecret) {
+ throw new NotFoundException("Secret key not available!");
+ }
+ return secret
+ ? new WrappedSecretKeyRing(blob, hasAnySecret, verified)
+ : new WrappedPublicKeyRing(blob, hasAnySecret, verified);
+ } else {
+ throw new NotFoundException("Key not found!");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
}
/**
* Saves PGPPublicKeyRing with its keys and userIds in DB
*/
@SuppressWarnings("unchecked")
- public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException {
- PGPPublicKey masterKey = keyRing.getPublicKey();
- long masterKeyId = masterKey.getKeyID();
+ public void savePublicKeyRing(UncachedKeyRing keyRing) throws IOException {
+ if (keyRing.isSecret()) {
+ throw new RuntimeException("Tried to save secret keyring as public! " +
+ "This is a bug, please file a bug report.");
+ }
+
+ UncachedPublicKey masterKey = keyRing.getPublicKey();
+ long masterKeyId = masterKey.getKeyId();
// IF there is a secret key, preserve it!
- PGPSecretKeyRing secretRing = null;
+ UncachedKeyRing secretRing = null;
try {
- secretRing = getPGPSecretKeyRing(masterKeyId);
+ secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
} catch (NotFoundException e) {
Log.e(Constants.TAG, "key not found!");
}
@@ -266,36 +257,38 @@ public class ProviderHelper {
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0;
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
+ for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
++rank;
}
// get a list of owned secret keys, for verification filtering
- LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
+ LongSparseArray<UncachedPublicKey> allKeyRings =
+ getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri());
// special case: available secret keys verify themselves!
- if (secretRing != null)
- allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
+ if (secretRing != null) {
+ allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
+ }
// classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved.
List<UserIdItem> uids = new ArrayList<UserIdItem>();
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
+ for (String userId : new IterableIterator<String>(
+ masterKey.getUnorderedUserIds().iterator())) {
UserIdItem item = new UserIdItem();
uids.add(item);
item.userId = userId;
// look through signatures for this specific key
- for (PGPSignature cert : new IterableIterator<PGPSignature>(
- masterKey.getSignaturesForID(userId))) {
- long certId = cert.getKeyID();
+ for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
+ masterKey.getSignaturesForId(userId))) {
+ long certId = cert.getKeyId();
try {
// self signature
if (certId == masterKeyId) {
- cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
- if (!cert.verifyCertification(userId, masterKey)) {
+ cert.init(masterKey);
+ if (!cert.verifySignature(masterKey, userId)) {
// not verified?! dang! TODO notify user? this is kinda serious...
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
continue;
@@ -304,31 +297,22 @@ public class ProviderHelper {
if (item.selfCert == null ||
item.selfCert.getCreationTime().before(cert.getCreationTime())) {
item.selfCert = cert;
- item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID();
- item.isRevoked =
- cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
+ item.isPrimary = cert.isPrimaryUserId();
+ item.isRevoked = cert.isRevocation();
}
}
// verify signatures from known private keys
if (allKeyRings.indexOfKey(certId) >= 0) {
- // mark them as verified
- cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME),
- allKeyRings.get(certId).getPublicKey());
- if (cert.verifyCertification(userId, masterKey)) {
+ cert.init(allKeyRings.get(certId));
+ if (cert.verifySignature(masterKey, userId)) {
item.trustedCerts.add(cert);
}
}
- } catch (SignatureException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Signature verification failed! "
- + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
+ + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
+ " from "
- + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
- } catch (PGPException e) {
- Log.e(Constants.TAG, "Signature verification failed! "
- + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
- + " from "
- + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
+ + PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e);
}
}
}
@@ -365,7 +349,7 @@ public class ProviderHelper {
// Save the saved keyring (if any)
if (secretRing != null) {
- saveKeyRing(secretRing);
+ saveSecretKeyRing(secretRing);
}
}
@@ -374,8 +358,8 @@ public class ProviderHelper {
String userId;
boolean isPrimary = false;
boolean isRevoked = false;
- PGPSignature selfCert;
- List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>();
+ WrappedSignature selfCert;
+ List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
@Override
public int compareTo(UserIdItem o) {
@@ -395,8 +379,13 @@ public class ProviderHelper {
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database!
*/
- public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException {
- long masterKeyId = keyRing.getPublicKey().getKeyID();
+ public void saveSecretKeyRing(UncachedKeyRing keyRing) throws IOException {
+ if (!keyRing.isSecret()) {
+ throw new RuntimeException("Tried to save publkc keyring as secret! " +
+ "This is a bug, please file a bug report.");
+ }
+
+ long masterKeyId = keyRing.getMasterKeyId();
{
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
@@ -408,14 +397,10 @@ public class ProviderHelper {
values.put(Keys.HAS_SECRET, 1);
// then, mark exactly the keys we have available
- for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- S2K s2k = sub.getS2K();
- // Set to 1, except if the encryption type is GNU_DUMMY_S2K
- if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
- mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
- Long.toString(sub.getKeyID())
- });
- }
+ for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
+ mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
+ Long.toString(sub)
+ });
}
// this implicitly leaves all keys which were not in the secret key ring
// with has_secret = 0
@@ -436,39 +421,39 @@ public class ProviderHelper {
/**
* Saves (or updates) a pair of public and secret KeyRings in the database
*/
- public void saveKeyRing(PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException {
- long masterKeyId = pubRing.getPublicKey().getKeyID();
+ public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException {
+ long masterKeyId = pubRing.getPublicKey().getKeyId();
- // delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below)
+ // delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below)
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
// save public keyring
- saveKeyRing(pubRing);
- saveKeyRing(privRing);
+ savePublicKeyRing(pubRing);
+ saveSecretKeyRing(secRing);
}
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private ContentProviderOperation
- buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException {
+ buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
ContentValues values = new ContentValues();
values.put(Keys.MASTER_KEY_ID, masterKeyId);
values.put(Keys.RANK, rank);
- values.put(Keys.KEY_ID, key.getKeyID());
+ values.put(Keys.KEY_ID, key.getKeyId());
values.put(Keys.KEY_SIZE, key.getBitStrength());
values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint());
- values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
- values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
- values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
- values.put(Keys.IS_REVOKED, key.isRevoked());
+ values.put(Keys.CAN_CERTIFY, key.canCertify());
+ values.put(Keys.CAN_SIGN, key.canSign());
+ values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
+ values.put(Keys.IS_REVOKED, key.maybeRevoked());
- values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
- Date expiryDate = PgpKeyHelper.getExpiryDate(key);
+ values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
+ Date expiryDate = key.getExpiryTime();
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
@@ -482,11 +467,12 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private ContentProviderOperation
- buildCertOperations(long masterKeyId, int rank, PGPSignature cert, int verified) throws IOException {
+ buildCertOperations(long masterKeyId, int rank, WrappedSignature cert, int verified)
+ throws IOException {
ContentValues values = new ContentValues();
values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank);
- values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
+ values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyId());
values.put(Certs.TYPE, cert.getSignatureType());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
values.put(Certs.VERIFIED, verified);
@@ -514,23 +500,11 @@ public class ProviderHelper {
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
- private String getKeyRingAsArmoredString(byte[] data) throws IOException {
- Object keyRing = null;
- if (data != null) {
- keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
- }
+ private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
+ UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = new ArmoredOutputStream(bos);
- aos.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- if (keyRing instanceof PGPSecretKeyRing) {
- aos.write(((PGPSecretKeyRing) keyRing).getEncoded());
- } else if (keyRing instanceof PGPPublicKeyRing) {
- aos.write(((PGPPublicKeyRing) keyRing).getEncoded());
- }
- aos.close();
-
+ keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
@@ -539,77 +513,12 @@ public class ProviderHelper {
}
public String getKeyRingAsArmoredString(Uri uri)
- throws NotFoundException, IOException {
+ throws NotFoundException, IOException, PgpGeneralException {
byte[] data = (byte[]) getGenericData(
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
return getKeyRingAsArmoredString(data);
}
- /**
- * TODO: currently not used, but will be needed to upload many keys at once!
- *
- * @param masterKeyIds
- * @return
- * @throws IOException
- */
- public ArrayList<String> getKeyRingsAsArmoredString(long[] masterKeyIds)
- throws IOException {
- ArrayList<String> output = new ArrayList<String>();
-
- if (masterKeyIds == null || masterKeyIds.length == 0) {
- Log.e(Constants.TAG, "No master keys given!");
- return output;
- }
-
- // Build a cursor for the selected masterKeyIds
- Cursor cursor;
- {
- String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
- for (int i = 0; i < masterKeyIds.length; ++i) {
- if (i != 0) {
- inMasterKeyList += ", ";
- }
- inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]);
- }
- inMasterKeyList += ")";
-
- cursor = mContentResolver.query(KeyRingData.buildPublicKeyRingUri(), new String[]{
- KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
- }, inMasterKeyList, null, null);
- }
-
- try {
- if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
- if (cursor.moveToFirst()) {
- do {
- Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
-
- byte[] data = cursor.getBlob(dataCol);
-
- // get actual keyring data blob and write it to ByteArrayOutputStream
- try {
- output.add(getKeyRingAsArmoredString(data));
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- }
- } while (cursor.moveToNext());
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- if (output.size() > 0) {
- return output;
- } else {
- return null;
- }
- }
-
public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index db2db668d..17c277026 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -419,14 +419,14 @@ public class OpenPgpService extends RemoteService {
try {
// try to find key, throws NotFoundException if not in db!
- mProviderHelper.getPGPPublicKeyRing(masterKeyId);
+ mProviderHelper.getWrappedPublicKeyRing(masterKeyId);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
// also return PendingIntent that opens the key view activity
Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(Long.toString(masterKeyId)));
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
intent,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 20223e319..cb58f8734 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -33,6 +33,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.EditKeyActivity;
@@ -179,9 +180,10 @@ public class AccountSettingsFragment extends Fragment implements
// select newly created key
try {
long masterKeyId = new ProviderHelper(getActivity())
- .extractOrGetMasterKeyId(data.getData());
+ .getCachedPublicKeyRing(data.getData())
+ .extractOrGetMasterKeyId();
mSelectKeyFragment.selectKey(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 2b8745f0a..ce706c84f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -26,14 +26,6 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
@@ -41,15 +33,19 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -61,7 +57,6 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -498,7 +493,7 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_SAVE_KEYRING.equals(action)) {
try {
/* Input */
- SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
+ OldSaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
String oldPassphrase = saveParcel.oldPassphrase;
String newPassphrase = saveParcel.newPassphrase;
boolean canSign = true;
@@ -511,33 +506,36 @@ public class KeychainIntentService extends IntentService
newPassphrase = oldPassphrase;
}
- long masterKeyId = saveParcel.keys.get(0).getKeyID();
+ long masterKeyId = saveParcel.keys.get(0).getKeyId();
/* Operation */
ProviderHelper providerHelper = new ProviderHelper(this);
if (!canSign) {
- PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
- PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
- keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
- oldPassphrase, newPassphrase);
+ setProgress(R.string.progress_building_key, 0, 100);
+ WrappedSecretKeyRing keyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
+ UncachedKeyRing newKeyRing =
+ keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase);
setProgress(R.string.progress_saving_key_ring, 50, 100);
- providerHelper.saveKeyRing(keyRing);
+ providerHelper.saveSecretKeyRing(newKeyRing);
setProgress(R.string.progress_done, 100, 100);
} else {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
- PgpKeyOperation.Pair<PGPSecretKeyRing, PGPPublicKeyRing> pair;
try {
- PGPSecretKeyRing privkey = providerHelper.getPGPSecretKeyRing(masterKeyId);
- PGPPublicKeyRing pubkey = providerHelper.getPGPPublicKeyRing(masterKeyId);
+ WrappedSecretKeyRing seckey = providerHelper.getWrappedSecretKeyRing(masterKeyId);
+ WrappedPublicKeyRing pubkey = providerHelper.getWrappedPublicKeyRing(masterKeyId);
- pair = keyOperations.buildSecretKey(privkey, pubkey, saveParcel); // edit existing
+ PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
+ keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ providerHelper.saveKeyRing(pair.first, pair.second);
} catch (ProviderHelper.NotFoundException e) {
- pair = keyOperations.buildNewSecretKey(saveParcel); //new Keyring
+ PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
+ keyOperations.buildNewSecretKey(saveParcel); //new Keyring
+ // save the pair
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ providerHelper.saveKeyRing(pair.first, pair.second);
}
- setProgress(R.string.progress_saving_key_ring, 90, 100);
- // save the pair
- providerHelper.saveKeyRing(pair.second, pair.first);
setProgress(R.string.progress_done, 100, 100);
}
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
@@ -557,13 +555,11 @@ public class KeychainIntentService extends IntentService
/* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
- PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
- passphrase, masterKey);
+ byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
/* Output */
Bundle resultData = new Bundle();
- resultData.putByteArray(RESULT_NEW_KEY,
- PgpConversionHelper.PGPSecretKeyToBytes(newKey));
+ resultData.putByteArray(RESULT_NEW_KEY, newKey);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -576,7 +572,6 @@ public class KeychainIntentService extends IntentService
try {
/* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
- ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */
@@ -589,24 +584,28 @@ public class KeychainIntentService extends IntentService
keysTotal);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
- PGPSecretKey masterKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ byte[] buf;
+
+ buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, true);
- newKeys.add(masterKey);
- keyUsageList.add(KeyFlags.CERTIFY_OTHER);
+ os.write(buf);
+ keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
keysCreated++;
setProgress(keysCreated, keysTotal);
- PGPSecretKey subKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
+ buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false);
- newKeys.add(subKey);
- keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
+ os.write(buf);
+ keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
keysCreated++;
setProgress(keysCreated, keysTotal);
- subKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
+ buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false);
- newKeys.add(subKey);
- keyUsageList.add(KeyFlags.SIGN_DATA);
+ os.write(buf);
+ keyUsageList.add(UncachedSecretKey.SIGN_DATA);
keysCreated++;
setProgress(keysCreated, keysTotal);
@@ -614,10 +613,8 @@ public class KeychainIntentService extends IntentService
// for sign
/* Output */
-
Bundle resultData = new Bundle();
- resultData.putByteArray(RESULT_NEW_KEY,
- PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
+ resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray());
resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -649,12 +646,10 @@ public class KeychainIntentService extends IntentService
}
} else if (ACTION_IMPORT_KEYRING.equals(action)) {
try {
- List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
-
- Bundle resultData = new Bundle();
+ List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
- resultData = pgpImportExport.importKeyRings(entries);
+ Bundle resultData = pgpImportExport.importKeyRings(entries);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
@@ -728,15 +723,12 @@ public class KeychainIntentService extends IntentService
HkpKeyserver server = new HkpKeyserver(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this);
- PGPPublicKeyRing keyring = (PGPPublicKeyRing) providerHelper.getPGPKeyRing(dataUri);
- if (keyring != null) {
- PgpImportExport pgpImportExport = new PgpImportExport(this, null);
-
- boolean uploaded = pgpImportExport.uploadKeyRingToServer(server,
- (PGPPublicKeyRing) keyring);
- if (!uploaded) {
- throw new PgpGeneralException("Unable to export key to selected server");
- }
+ WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri);
+ PgpImportExport pgpImportExport = new PgpImportExport(this, null);
+
+ boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
+ if (!uploaded) {
+ throw new PgpGeneralException("Unable to export key to selected server");
}
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
@@ -746,9 +738,11 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_KEYS.equals(action)) {
try {
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
- String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
// this downloads the keys and places them into the ImportKeysListEntry entries
+ String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
+
+ ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) {
Keyserver server;
@@ -770,49 +764,15 @@ public class KeychainIntentService extends IntentService
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
}
- // create PGPKeyRing object based on downloaded armored key
- PGPKeyRing downloadedKey = null;
- BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
- if (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // get first object in block
- Object obj;
- if ((obj = objectFactory.nextObject()) != null) {
-
- if (obj instanceof PGPKeyRing) {
- downloadedKey = (PGPKeyRing) obj;
- } else {
- throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
- }
- }
- }
-
- // verify downloaded key by comparing fingerprints
- if (entry.getFingerprintHex() != null) {
- String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
- downloadedKey.getPublicKey().getFingerprint());
- if (downloadedKeyFp.equalsIgnoreCase(entry.getFingerprintHex())) {
- Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
- "the requested fingerprint!");
- } else {
- throw new PgpGeneralException("fingerprint of downloaded key is " +
- "NOT the same as the requested fingerprint!");
- }
- }
-
// save key bytes in entry object for doing the
// actual import afterwards
- entry.setBytes(downloadedKey.getEncoded());
+ keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
}
-
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
- importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
+ importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@@ -839,29 +799,18 @@ public class KeychainIntentService extends IntentService
}
ProviderHelper providerHelper = new ProviderHelper(this);
- PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
- PGPPublicKeyRing publicRing = providerHelper.getPGPPublicKeyRing(pubKeyId);
- PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
- PGPSecretKeyRing secretKeyRing = null;
- try {
- secretKeyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- // TODO: throw exception here!
+ WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId);
+ WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
+ WrappedSecretKey certificationKey = secretKeyRing.getSubKey();
+ if(!certificationKey.unlock(signaturePassphrase)) {
+ throw new PgpGeneralException("Error extracting key (bad passphrase?)");
}
- PGPSecretKey certificationKey = PgpKeyHelper.getFirstCertificationSubkey(secretKeyRing);
- publicKey = keyOperation.certifyKey(certificationKey, publicKey,
- userIds, signaturePassphrase);
- publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
+ UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
// store the signed key in our local cache
- PgpImportExport pgpImportExport = new PgpImportExport(this, null);
- int retval = pgpImportExport.storeKeyRingInCache(publicRing);
- if (retval != PgpImportExport.RETURN_OK && retval != PgpImportExport.RETURN_UPDATED) {
- throw new PgpGeneralException("Failed to store signed key in local cache");
- }
-
+ providerHelper.savePublicKeyRing(newRing);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+
} catch (Exception e) {
sendErrorToHandler(e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java
new file mode 100644
index 000000000..b722393ad
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+/** Class for parcelling data between ui and services.
+ * This class is outdated and scheduled for removal, pending a rewrite of the
+ * EditKeyActivity and save keyring routines.
+ */
+@Deprecated
+public class OldSaveKeyringParcel implements Parcelable {
+
+ public ArrayList<String> userIds;
+ public ArrayList<String> originalIDs;
+ public ArrayList<String> deletedIDs;
+ public boolean[] newIDs;
+ public boolean primaryIDChanged;
+ public boolean[] moddedKeys;
+ public ArrayList<UncachedSecretKey> deletedKeys;
+ public ArrayList<Calendar> keysExpiryDates;
+ public ArrayList<Integer> keysUsages;
+ public String newPassphrase;
+ public String oldPassphrase;
+ public boolean[] newKeys;
+ public ArrayList<UncachedSecretKey> keys;
+ public String originalPrimaryID;
+
+ public OldSaveKeyringParcel() {}
+
+ private OldSaveKeyringParcel(Parcel source) {
+ userIds = (ArrayList<String>) source.readSerializable();
+ originalIDs = (ArrayList<String>) source.readSerializable();
+ deletedIDs = (ArrayList<String>) source.readSerializable();
+ newIDs = source.createBooleanArray();
+ primaryIDChanged = source.readByte() != 0;
+ moddedKeys = source.createBooleanArray();
+ byte[] tmp = source.createByteArray();
+ if (tmp == null) {
+ deletedKeys = null;
+ } else {
+ deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
+ }
+ keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
+ keysUsages = source.readArrayList(Integer.class.getClassLoader());
+ newPassphrase = source.readString();
+ oldPassphrase = source.readString();
+ newKeys = source.createBooleanArray();
+ keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
+ originalPrimaryID = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeSerializable(userIds); //might not be the best method to store.
+ destination.writeSerializable(originalIDs);
+ destination.writeSerializable(deletedIDs);
+ destination.writeBooleanArray(newIDs);
+ destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
+ destination.writeBooleanArray(moddedKeys);
+ destination.writeByteArray(encodeArrayList(deletedKeys));
+ destination.writeSerializable(keysExpiryDates);
+ destination.writeList(keysUsages);
+ destination.writeString(newPassphrase);
+ destination.writeString(oldPassphrase);
+ destination.writeBooleanArray(newKeys);
+ destination.writeByteArray(encodeArrayList(keys));
+ destination.writeString(originalPrimaryID);
+ }
+
+ public static final Creator<OldSaveKeyringParcel> CREATOR = new Creator<OldSaveKeyringParcel>() {
+ public OldSaveKeyringParcel createFromParcel(final Parcel source) {
+ return new OldSaveKeyringParcel(source);
+ }
+
+ public OldSaveKeyringParcel[] newArray(final int size) {
+ return new OldSaveKeyringParcel[size];
+ }
+ };
+
+ private static byte[] encodeArrayList(ArrayList<UncachedSecretKey> list) {
+ if(list.isEmpty()) {
+ return null;
+ }
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for(UncachedSecretKey key : new IterableIterator<UncachedSecretKey>(list.iterator())) {
+ try {
+ key.encodeSecretKey(os);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting ArrayList<UncachedSecretKey> to byte[]!", e);
+ }
+ }
+ return os.toByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index db4fecef0..d42bae67a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -34,20 +34,14 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
-import java.util.Iterator;
/**
* This service runs in its own process, but is available to all other processes as the main
@@ -163,81 +157,47 @@ public class PassphraseCacheService extends Service {
* @return
*/
private String getCachedPassphraseImpl(long keyId) {
- Log.d(TAG, "getCachedPassphraseImpl() get masterKeyId for " + keyId);
-
- // try to get master key id which is used as an identifier for cached passphrases
- long masterKeyId = keyId;
- if (masterKeyId != Constants.key.symmetric) {
- try {
- masterKeyId = new ProviderHelper(this).getMasterKeyId(
- KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)));
- } catch (ProviderHelper.NotFoundException e) {
+ // passphrase for symmetric encryption?
+ if (keyId == Constants.key.symmetric) {
+ Log.d(TAG, "getCachedPassphraseImpl() for symmetric encryption");
+ String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
+ if (cachedPassphrase == null) {
return null;
}
+ addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase);
+ return cachedPassphrase;
}
- Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
- // get cached passphrase
- String cachedPassphrase = mPassphraseCache.get(masterKeyId);
- if (cachedPassphrase == null) {
- // if key has no passphrase -> cache and return empty passphrase
- if (!hasPassphrase(this, masterKeyId)) {
+ // try to get master key id which is used as an identifier for cached passphrases
+ try {
+ Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + keyId);
+ WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
+ // no passphrase needed? just add empty string and return it, then
+ if (!key.hasPassphrase()) {
Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
- addCachedPassphrase(this, masterKeyId, "");
+ addCachedPassphrase(this, keyId, "");
return "";
- } else {
- return null;
}
- }
- // set it again to reset the cache life cycle
- Log.d(TAG, "Cache passphrase again when getting it!");
- addCachedPassphrase(this, masterKeyId, cachedPassphrase);
- return cachedPassphrase;
- }
-
- public static boolean hasPassphrase(PGPSecretKeyRing secretKeyRing) {
- PGPSecretKey secretKey = null;
- boolean foundValidKey = false;
- for (Iterator keys = secretKeyRing.getSecretKeys(); keys.hasNext(); ) {
- secretKey = (PGPSecretKey) keys.next();
- if (!secretKey.isPrivateKeyEmpty()) {
- foundValidKey = true;
- break;
+ // get cached passphrase
+ String cachedPassphrase = mPassphraseCache.get(keyId);
+ if (cachedPassphrase == null) {
+ Log.d(TAG, "Passphrase not (yet) cached, returning null");
+ // not really an error, just means the passphrase is not cached but not empty either
+ return null;
}
- }
- if(!foundValidKey) {
- return false;
- }
- try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider("SC").build("".toCharArray());
- PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
- return testKey == null;
- } catch(PGPException e) {
- // this means the crc check failed -> passphrase required
- return true;
- }
- }
+ // set it again to reset the cache life cycle
+ Log.d(TAG, "Cache passphrase again when getting it!");
+ addCachedPassphrase(this, keyId, cachedPassphrase);
+ return cachedPassphrase;
- /**
- * Checks if key has a passphrase.
- *
- * @param secretKeyId
- * @return true if it has a passphrase
- */
- public static boolean hasPassphrase(Context context, long secretKeyId) {
- // check if the key has no passphrase
- try {
- PGPSecretKeyRing secRing = new ProviderHelper(context).getPGPSecretKeyRing(secretKeyId);
- return hasPassphrase(secRing);
} catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
+ Log.e(TAG, "Passphrase for unknown key was requested!");
+ return null;
}
-
- return true;
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 3f0b37b75..3514ab2e5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -1,92 +1,101 @@
-/*
- * Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+/** This class is a a transferable representation for a collection of changes
+ * to be done on a keyring.
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This class should include all types of operations supported in the backend.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * All changes are done in a differential manner. Besides the two key
+ * identification attributes, all attributes may be null, which indicates no
+ * change to the keyring. This is also the reason why boxed values are used
+ * instead of primitives in the subclasses.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * Application of operations in the backend should be fail-fast, which means an
+ * error in any included operation (for example revocation of a non-existent
+ * subkey) will cause the operation as a whole to fail.
*/
+public class SaveKeyringParcel implements Parcelable {
-package org.sufficientlysecure.keychain.service;
+ // the master key id to be edited
+ private final long mMasterKeyId;
+ // the key fingerprint, for safety
+ private final byte[] mFingerprint;
-import android.os.Parcel;
-import android.os.Parcelable;
+ public String newPassphrase;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+ public String[] addUserIds;
+ public SubkeyAdd[] addSubKeys;
-import java.util.ArrayList;
-import java.util.Calendar;
+ public HashMap<Long, SubkeyChange> changeSubKeys;
+ public String changePrimaryUserId;
-public class SaveKeyringParcel implements Parcelable {
+ public String[] revokeUserIds;
+ public long[] revokeSubKeys;
- public ArrayList<String> userIds;
- public ArrayList<String> originalIDs;
- public ArrayList<String> deletedIDs;
- public boolean[] newIDs;
- public boolean primaryIDChanged;
- public boolean[] moddedKeys;
- public ArrayList<PGPSecretKey> deletedKeys;
- public ArrayList<Calendar> keysExpiryDates;
- public ArrayList<Integer> keysUsages;
- public String newPassphrase;
- public String oldPassphrase;
- public boolean[] newKeys;
- public ArrayList<PGPSecretKey> keys;
- public String originalPrimaryID;
-
- public SaveKeyringParcel() {}
-
- private SaveKeyringParcel(Parcel source) {
- userIds = (ArrayList<String>) source.readSerializable();
- originalIDs = (ArrayList<String>) source.readSerializable();
- deletedIDs = (ArrayList<String>) source.readSerializable();
- newIDs = source.createBooleanArray();
- primaryIDChanged = source.readByte() != 0;
- moddedKeys = source.createBooleanArray();
- byte[] tmp = source.createByteArray();
- if (tmp == null) {
- deletedKeys = null;
- } else {
- deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
+ public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
+ mMasterKeyId = masterKeyId;
+ mFingerprint = fingerprint;
+ }
+
+ // performance gain for using Parcelable here would probably be negligible,
+ // use Serializable instead.
+ public static class SubkeyAdd implements Serializable {
+ public final int mAlgorithm;
+ public final int mKeysize;
+ public final int mFlags;
+ public final Long mExpiry;
+ public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
+ mAlgorithm = algorithm;
+ mKeysize = keysize;
+ mFlags = flags;
+ mExpiry = expiry;
+ }
+ }
+
+ public static class SubkeyChange implements Serializable {
+ public final long mKeyId;
+ public final Integer mFlags;
+ public final Long mExpiry;
+ public SubkeyChange(long keyId, Integer flags, Long expiry) {
+ mKeyId = keyId;
+ mFlags = flags;
+ mExpiry = expiry;
}
- keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
- keysUsages = source.readArrayList(Integer.class.getClassLoader());
- newPassphrase = source.readString();
- oldPassphrase = source.readString();
- newKeys = source.createBooleanArray();
- keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
- originalPrimaryID = source.readString();
+ }
+
+ public SaveKeyringParcel(Parcel source) {
+ mMasterKeyId = source.readLong();
+ mFingerprint = source.createByteArray();
+
+ addUserIds = source.createStringArray();
+ addSubKeys = (SubkeyAdd[]) source.readSerializable();
+
+ changeSubKeys = (HashMap<Long,SubkeyChange>) source.readSerializable();
+ changePrimaryUserId = source.readString();
+
+ revokeUserIds = source.createStringArray();
+ revokeSubKeys = source.createLongArray();
}
@Override
public void writeToParcel(Parcel destination, int flags) {
- destination.writeSerializable(userIds); //might not be the best method to store.
- destination.writeSerializable(originalIDs);
- destination.writeSerializable(deletedIDs);
- destination.writeBooleanArray(newIDs);
- destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
- destination.writeBooleanArray(moddedKeys);
- byte[] tmp = null;
- if (deletedKeys.size() != 0) {
- tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys);
- }
- destination.writeByteArray(tmp);
- destination.writeSerializable(keysExpiryDates);
- destination.writeList(keysUsages);
- destination.writeString(newPassphrase);
- destination.writeString(oldPassphrase);
- destination.writeBooleanArray(newKeys);
- destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
- destination.writeString(originalPrimaryID);
+ destination.writeLong(mMasterKeyId);
+ destination.writeByteArray(mFingerprint);
+
+ destination.writeStringArray(addUserIds);
+ destination.writeSerializable(addSubKeys);
+
+ destination.writeSerializable(changeSubKeys);
+ destination.writeString(changePrimaryUserId);
+
+ destination.writeStringArray(revokeUserIds);
+ destination.writeLongArray(revokeSubKeys);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@@ -103,4 +112,5 @@ public class SaveKeyringParcel implements Parcelable {
public int describeContents() {
return 0;
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index bd12a3b52..6c74818a5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -43,7 +43,6 @@ import android.widget.TextView;
import com.devspark.appmsg.AppMsg;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@@ -51,7 +50,6 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@@ -222,54 +220,22 @@ public class CertifyKeyActivity extends ActionBarActivity implements
* handles the UI bits of the signing process on the UI thread
*/
private void initiateSigning() {
- try {
- PGPPublicKeyRing pubring = new ProviderHelper(this).getPGPPublicKeyRing(mPubKeyId);
-
- // if we have already signed this key, dont bother doing it again
- boolean alreadySigned = false;
-
- /* todo: reconsider this at a later point when certs are in the db
- @SuppressWarnings("unchecked")
- Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
- while (itr.hasNext()) {
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == mMasterKeyId) {
- alreadySigned = true;
- break;
- }
- }
- */
-
- if (!alreadySigned) {
- /*
- * get the user's passphrase for this key (if required)
- */
- String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
- if (passphrase == null) {
- PassphraseDialogFragment.show(this, mMasterKeyId,
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- startSigning();
- }
- }
+ // get the user's passphrase for this key (if required)
+ String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
+ if (passphrase == null) {
+ PassphraseDialogFragment.show(this, mMasterKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ startSigning();
}
- );
- // bail out; need to wait until the user has entered the passphrase before trying again
- return;
- } else {
- startSigning();
- }
- } else {
- AppMsg.makeText(this, R.string.key_has_already_been_certified, AppMsg.STYLE_ALERT)
- .show();
-
- setResult(RESULT_CANCELED);
- finish();
- }
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
+ }
+ });
+ // bail out; need to wait until the user has entered the passphrase before trying again
+ return;
+ } else {
+ startSigning();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index bd3a98567..d9c7a1736 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -44,21 +43,22 @@ import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.ExportHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
@@ -67,7 +67,6 @@ import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
import org.sufficientlysecure.keychain.ui.widget.SectionView;
import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
-import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -89,8 +88,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// EDIT
private Uri mDataUri;
- private PGPSecretKeyRing mKeyRing = null;
-
private SectionView mUserIdsView;
private SectionView mKeysView;
@@ -106,7 +103,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
private CheckBox mNoPassphrase;
Vector<String> mUserIds;
- Vector<PGPSecretKey> mKeys;
+ Vector<UncachedSecretKey> mKeys;
Vector<Integer> mKeysUsages;
boolean mMasterCanSign = true;
@@ -159,7 +156,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
);
mUserIds = new Vector<String>();
- mKeys = new Vector<PGPSecretKey>();
+ mKeys = new Vector<UncachedSecretKey>();
mKeysUsages = new Vector<Integer>();
// Catch Intents opened from other apps
@@ -240,7 +237,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// get new key from data bundle returned from service
Bundle data = message.getData();
- ArrayList<PGPSecretKey> newKeys =
+ ArrayList<UncachedSecretKey> newKeys =
PgpConversionHelper.BytesToPGPSecretKeyList(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
@@ -288,18 +285,18 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Log.d(Constants.TAG, "uri: " + mDataUri);
try {
- Uri secretUri = KeychainContract.KeyRingData.buildSecretKeyRingUri(mDataUri);
- mKeyRing = (PGPSecretKeyRing) new ProviderHelper(this).getPGPKeyRing(secretUri);
-
- PGPSecretKey masterKey = mKeyRing.getSecretKey();
- mMasterCanSign = PgpKeyHelper.isCertificationKey(mKeyRing.getSecretKey());
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mKeyRing.getSecretKeys())) {
- mKeys.add(key);
- mKeysUsages.add(-1); // get usage when view is created
+ Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri);
+
+ mMasterCanSign = keyRing.getSubKey().canCertify();
+ for (WrappedSecretKey key : keyRing.iterator()) {
+ // Turn into uncached instance
+ mKeys.add(key.getUncached());
+ mKeysUsages.add(key.getKeyUsage()); // get usage when view is created
}
boolean isSet = false;
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
+ for (String userId : keyRing.getSubKey().getUserIds()) {
Log.d(Constants.TAG, "Added userId " + userId);
if (!isSet) {
isSet = true;
@@ -314,7 +311,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
buildLayout(false);
mCurrentPassphrase = "";
- mIsPassphraseSet = PassphraseCacheService.hasPassphrase(mKeyRing);
+ mIsPassphraseSet = keyRing.hasPassphrase();
if (!mIsPassphraseSet) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
@@ -432,7 +429,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
if (mKeysView.getEditors().getChildCount() == 0) {
return 0;
}
- return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID();
+ return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId();
}
public boolean isPassphraseSet() {
@@ -556,7 +553,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
- SaveKeyringParcel saveParams = new SaveKeyringParcel();
+ OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel();
saveParams.userIds = getUserIds(mUserIdsView);
saveParams.originalIDs = mUserIdsView.getOriginalIDs();
saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
@@ -572,7 +569,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
saveParams.keys = getKeys(mKeysView);
saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
-
// fill values for this action
Bundle data = new Bundle();
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
@@ -591,8 +587,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Intent data = new Intent();
// return uri pointing to new created key
- Uri uri = KeychainContract.KeyRings.buildGenericKeyRingUri(
- String.valueOf(getMasterKeyId()));
+ Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId());
data.setData(uri);
setResult(RESULT_OK, data);
@@ -690,8 +685,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
* @param keysView
* @return
*/
- private ArrayList<PGPSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
- ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
+ private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
+ ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
ViewGroup keyEditors = keysView.getEditors();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index c954e6465..03483575c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -30,11 +30,11 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
@@ -144,20 +144,17 @@ public class EncryptAsymmetricFragment extends Fragment {
*/
private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds,
ProviderHelper providerHelper) {
+ // TODO all of this works under the assumption that the first suitable subkey is always used!
+ // not sure if we need to distinguish between different subkeys here?
if (preselectedSignatureKeyId != 0) {
- // TODO: don't use bouncy castle objects!
try {
- PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRingWithKeyId(
- preselectedSignatureKeyId);
-
- PGPSecretKey masterKey = keyRing.getSecretKey();
- if (masterKey != null) {
- PGPSecretKey signKey = PgpKeyHelper.getFirstSigningSubkey(keyRing);
- if (signKey != null) {
- setSignatureKeyId(masterKey.getKeyID());
- }
+ CachedPublicKeyRing keyring =
+ providerHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingUri(preselectedSignatureKeyId));
+ if(keyring.hasAnySecret()) {
+ setSignatureKeyId(keyring.getMasterKeyId());
}
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
@@ -165,14 +162,13 @@ public class EncryptAsymmetricFragment extends Fragment {
if (preselectedEncryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
- // TODO One query per selected key?! wtf
try {
- long id = providerHelper.getMasterKeyId(
+ long id = providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(preselectedEncryptionKeyIds[i]))
- );
+ preselectedEncryptionKeyIds[i])
+ ).getMasterKeyId();
goodIds.add(id);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 35076287b..d33d450f9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -42,6 +42,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -415,8 +416,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
// fill values for this action
Bundle data = new Bundle();
- // get selected key entries
- ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ // get DATA from selected key entries
+ ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -442,7 +443,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, mListFragment.getKeyServer());
// get selected key entries
- ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -466,7 +467,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
Bundle data = new Bundle();
// get selected key entries
- ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index d9bd9b782..b70dd4d80 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
+import android.support.v4.util.LongSparseArray;
import android.view.View;
import android.widget.ListView;
@@ -31,6 +32,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@@ -67,6 +69,8 @@ public class ImportKeysListFragment extends ListFragment implements
private static final int LOADER_ID_SERVER_QUERY = 1;
private static final int LOADER_ID_KEYBASE = 2;
+ private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
+
public byte[] getKeyBytes() {
return mKeyBytes;
}
@@ -91,8 +95,16 @@ public class ImportKeysListFragment extends ListFragment implements
return mAdapter.getData();
}
- public ArrayList<ImportKeysListEntry> getSelectedData() {
- return mAdapter.getSelectedData();
+ public ArrayList<ParcelableKeyRing> getSelectedData() {
+ ArrayList<ParcelableKeyRing> result = new ArrayList<ParcelableKeyRing>();
+ for(ImportKeysListEntry entry : getSelectedEntries()) {
+ result.add(mCachedKeyData.get(entry.getKeyId()));
+ }
+ return result;
+ }
+
+ public ArrayList<ImportKeysListEntry> getSelectedEntries() {
+ return mAdapter.getSelectedEntries();
}
/**
@@ -120,8 +132,7 @@ public class ImportKeysListFragment extends ListFragment implements
mActivity = getActivity();
- // Give some text to display if there is no data. In a real
- // application this would come from a resource.
+ // Give some text to display if there is no data.
setEmptyText(mActivity.getString(R.string.error_nothing_import));
// Create an empty adapter we will use to display the loaded data.
@@ -252,11 +263,15 @@ public class ImportKeysListFragment extends ListFragment implements
Exception error = data.getError();
+ // free old cached key data
+ mCachedKeyData = null;
+
switch (loader.getId()) {
case LOADER_ID_BYTES:
if (error == null) {
// No error
+ mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
} else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
AppMsg.makeText(getActivity(), R.string.error_import_file_no_content,
AppMsg.STYLE_ALERT).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index e38ed6e67..9c90b5eb7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -317,7 +317,7 @@ public class KeyListFragment extends LoaderFragment
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
viewIntent.setData(
- KeyRings.buildGenericKeyRingUri(Long.toString(mAdapter.getMasterKeyId(position))));
+ KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
startActivity(viewIntent);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 38a0c8478..9ddc8e3e1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
@@ -84,7 +84,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position);
- Uri result = KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
+ Uri result = KeyRings.buildGenericKeyRingUri(masterKeyId);
// return data to activity, which results in finishing it
mActivity.afterListSelection(result);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
index 8db750917..fe2ecf3a3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
@@ -132,7 +132,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan
//For AppSettingsFragment
public void selectKey(long masterKeyId) {
- Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
+ Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(masterKeyId);
mReceivedUri = buildUri;
getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index f78c30820..ae0206ab1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -32,24 +32,17 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
-import org.spongycastle.bcpg.SignatureSubpacket;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.bcpg.sig.RevocationReason;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
-import java.security.SignatureException;
import java.util.Date;
public class ViewCertActivity extends ActionBarActivity
@@ -146,32 +139,25 @@ public class ViewCertActivity extends ActionBarActivity
mCertifierUid.setText(R.string.unknown_uid);
}
- PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA));
+ WrappedSignature sig = WrappedSignature.fromBytes(data.getBlob(INDEX_DATA));
try {
ProviderHelper providerHelper = new ProviderHelper(this);
- PGPKeyRing signeeRing = providerHelper.getPGPKeyRing(
- KeychainContract.KeyRingData.buildPublicKeyRingUri(
- Long.toString(data.getLong(INDEX_MASTER_KEY_ID)))
- );
- PGPKeyRing signerRing = providerHelper.getPGPKeyRing(
- KeychainContract.KeyRingData.buildPublicKeyRingUri(
- Long.toString(sig.getKeyID()))
- );
+
+ WrappedPublicKeyRing signeeRing =
+ providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
+ WrappedPublicKeyRing signerRing =
+ providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
try {
- sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME), signerRing.getPublicKey());
- if (sig.verifyCertification(signeeUid, signeeRing.getPublicKey())) {
+ sig.init(signerRing.getSubkey());
+ if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) {
mStatus.setText(R.string.cert_verify_ok);
mStatus.setTextColor(getResources().getColor(R.color.bbutton_success));
} else {
mStatus.setText(R.string.cert_verify_failed);
mStatus.setTextColor(getResources().getColor(R.color.alert));
}
- } catch (SignatureException e) {
- mStatus.setText(R.string.cert_verify_error);
- mStatus.setTextColor(getResources().getColor(R.color.alert));
- } catch (PGPException e) {
+ } catch (PgpGeneralException e) {
mStatus.setText(R.string.cert_verify_error);
mStatus.setTextColor(getResources().getColor(R.color.alert));
}
@@ -185,29 +171,26 @@ public class ViewCertActivity extends ActionBarActivity
mRowReason.setVisibility(View.GONE);
switch (data.getInt(INDEX_TYPE)) {
- case PGPSignature.DEFAULT_CERTIFICATION:
+ case WrappedSignature.DEFAULT_CERTIFICATION:
mType.setText(R.string.cert_default);
break;
- case PGPSignature.NO_CERTIFICATION:
+ case WrappedSignature.NO_CERTIFICATION:
mType.setText(R.string.cert_none);
break;
- case PGPSignature.CASUAL_CERTIFICATION:
+ case WrappedSignature.CASUAL_CERTIFICATION:
mType.setText(R.string.cert_casual);
break;
- case PGPSignature.POSITIVE_CERTIFICATION:
+ case WrappedSignature.POSITIVE_CERTIFICATION:
mType.setText(R.string.cert_positive);
break;
- case PGPSignature.CERTIFICATION_REVOCATION: {
+ case WrappedSignature.CERTIFICATION_REVOCATION: {
mType.setText(R.string.cert_revoke);
- if (sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) {
- SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket(
- SignatureSubpacketTags.REVOCATION_REASON);
- // For some reason, this is missing in SignatureSubpacketInputStream:146
- if (!(p instanceof RevocationReason)) {
- p = new RevocationReason(false, p.getData());
+ if (sig.isRevocation()) {
+ try {
+ mReason.setText(sig.getRevocationReason());
+ } catch(PgpGeneralException e) {
+ mReason.setText(R.string.none);
}
- String reason = ((RevocationReason) p).getRevocationDescription();
- mReason.setText(reason);
mRowReason.setVisibility(View.VISIBLE);
}
break;
@@ -223,14 +206,11 @@ public class ViewCertActivity extends ActionBarActivity
try {
ProviderHelper providerHelper = new ProviderHelper(ViewCertActivity.this);
- long signerMasterKeyId = providerHelper.getMasterKeyId(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mCertifierKeyId))
- );
- viewIntent.setData(KeyRings.buildGenericKeyRingUri(
- Long.toString(signerMasterKeyId))
- );
+ long signerMasterKeyId = providerHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mCertifierKeyId)).getMasterKeyId();
+ viewIntent.setData(KeyRings.buildGenericKeyRingUri(signerMasterKeyId));
startActivity(viewIntent);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
// TODO notify user of this, maybe offer download?
Log.e(Constants.TAG, "key not found!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 8c52e6f22..bed116f5f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -326,10 +326,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
try {
Uri blobUri =
KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
- mNfcKeyringBytes = mProviderHelper.getPGPKeyRing(
- blobUri).getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error parsing keyring", e);
+ mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData(
+ blobUri,
+ KeychainContract.KeyRingData.KEY_RING_DATA,
+ ProviderHelper.FIELD_TYPE_BLOB);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index d5658586d..3cd43638a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -33,10 +33,10 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
-import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@@ -227,19 +227,19 @@ public class ViewKeyCertsFragment extends LoaderFragment
wSignerKeyId.setText(signerKeyId);
switch (cursor.getInt(mIndexType)) {
- case PGPSignature.DEFAULT_CERTIFICATION: // 0x10
+ case WrappedSignature.DEFAULT_CERTIFICATION: // 0x10
wSignStatus.setText(R.string.cert_default);
break;
- case PGPSignature.NO_CERTIFICATION: // 0x11
+ case WrappedSignature.NO_CERTIFICATION: // 0x11
wSignStatus.setText(R.string.cert_none);
break;
- case PGPSignature.CASUAL_CERTIFICATION: // 0x12
+ case WrappedSignature.CASUAL_CERTIFICATION: // 0x12
wSignStatus.setText(R.string.cert_casual);
break;
- case PGPSignature.POSITIVE_CERTIFICATION: // 0x13
+ case WrappedSignature.POSITIVE_CERTIFICATION: // 0x13
wSignStatus.setText(R.string.cert_positive);
break;
- case PGPSignature.CERTIFICATION_REVOCATION: // 0x30
+ case WrappedSignature.CERTIFICATION_REVOCATION: // 0x30
wSignStatus.setText(R.string.cert_revoke);
break;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index 026417776..c16bb82af 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -32,7 +32,9 @@ import android.widget.ListView;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -235,14 +237,16 @@ public class ViewKeyMainFragment extends LoaderFragment implements
return;
}
try {
- long keyId = new ProviderHelper(getActivity()).extractOrGetMasterKeyId(dataUri);
+ long keyId = new ProviderHelper(getActivity())
+ .getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId};
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
index b3655133d..a264a804f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
@@ -190,6 +191,9 @@ public class ViewKeyShareFragment extends LoaderFragment implements
}
startActivity(Intent.createChooser(sendIntent, title));
}
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "error processing key!", e);
+ AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
} catch (IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 9573efdfe..114c6afae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -32,6 +32,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Highlighter;
@@ -83,7 +84,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
return mData;
}
- public ArrayList<ImportKeysListEntry> getSelectedData() {
+ public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>();
for (ImportKeysListEntry entry : mData) {
if (entry.isSelected()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index b6c829677..03a82696d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -19,19 +19,19 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
+import android.support.v4.util.LongSparseArray;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream;
-import java.io.InputStream;
import java.util.ArrayList;
+import java.util.List;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
@@ -56,6 +56,7 @@ public class ImportKeysListLoader
final InputData mInputData;
ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>();
+ LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<ParcelableKeyRing>();
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListLoader(Context context, InputData inputData) {
@@ -107,6 +108,10 @@ public class ImportKeysListLoader
super.deliverResult(data);
}
+ public LongSparseArray<ParcelableKeyRing> getParcelableRings() {
+ return mParcelableRings;
+ }
+
/**
* Reads all PGPKeyRing objects from input
*
@@ -116,7 +121,6 @@ public class ImportKeysListLoader
private void generateListOfKeyrings(InputData inputData) {
boolean isEmpty = true;
- int nonPgpCounter = 0;
PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream());
@@ -129,28 +133,18 @@ public class ImportKeysListLoader
// read all available blocks... (asc files can contain many blocks with BEGIN END)
while (bufferedInput.available() > 0) {
- isEmpty = false;
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing newKeyring = (PGPKeyRing) obj;
- addToData(newKeyring);
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- nonPgpCounter++;
- }
+ // todo deal with non-keyring objects?
+ List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
+ for(UncachedKeyRing key : rings) {
+ ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);
+ mData.add(item);
+ mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded()));
+ isEmpty = false;
}
}
} catch (Exception e) {
Log.e(Constants.TAG, "Exception on parsing key file!", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
- nonPgpCounter = 0;
}
if (isEmpty) {
@@ -158,16 +152,6 @@ public class ImportKeysListLoader
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, new FileHasNoContent());
}
-
- if (nonPgpCounter > 0) {
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
- (mData, new NonPgpPart(nonPgpCounter));
- }
- }
-
- private void addToData(PGPKeyRing keyring) {
- ImportKeysListEntry item = new ImportKeysListEntry(getContext(), keyring);
- mData.add(item);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 12fd77141..5f47ee13c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -41,17 +41,12 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.Log;
@@ -106,8 +101,12 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
long secretKeyId) throws PgpGeneralException {
// check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
- if (!PassphraseCacheService.hasPassphrase(context, secretKeyId)) {
- throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
+ try {
+ if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
+ throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
+ }
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException("Error: Key not found!", e);
}
}
@@ -139,18 +138,24 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
alert.setTitle(R.string.title_authentication);
- final PGPSecretKey secretKey;
- final String userId;
+ final WrappedSecretKeyRing secretRing;
+ String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) {
- secretKey = null;
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
+ secretRing = null;
} else {
try {
ProviderHelper helper = new ProviderHelper(activity);
- secretKey = helper.getPGPSecretKeyRing(secretKeyId).getSecretKey();
- userId = (String) helper.getUnifiedData(secretKeyId,
- KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ secretRing = helper.getWrappedSecretKeyRing(secretKeyId);
+ // yes the inner try/catch block is necessary, otherwise the final variable
+ // above can't be statically verified to have been set in all cases because
+ // the catch clause doesn't return.
+ try {
+ userId = secretRing.getPrimaryUserId();
+ } catch (PgpGeneralException e) {
+ userId = null;
+ }
} catch (ProviderHelper.NotFoundException e) {
alert.setTitle(R.string.title_key_not_found);
alert.setMessage(getString(R.string.key_not_found, secretKeyId));
@@ -179,76 +184,59 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
- long curKeyIndex = 1;
- boolean keyOK = true;
+
String passphrase = mPassphraseEditText.getText().toString();
- long keyId;
- PGPSecretKey clickSecretKey = secretKey;
-
- if (clickSecretKey != null) {
- while (keyOK) {
- if (clickSecretKey != null) { // check again for loop
- try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- passphrase.toCharArray());
- PGPPrivateKey testKey = clickSecretKey
- .extractPrivateKey(keyDecryptor);
- if (testKey == null) {
- if (!clickSecretKey.isMasterKey()) {
- Toast.makeText(activity,
- R.string.error_could_not_extract_private_key,
- Toast.LENGTH_SHORT).show();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- return;
- } else {
- try {
- clickSecretKey = PgpKeyHelper.getKeyNum(new ProviderHelper(activity)
- .getPGPSecretKeyRingWithKeyId(secretKeyId),
- curKeyIndex
- );
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- }
- curKeyIndex++; // does post-increment work like C?
- continue;
- }
- } else {
- keyOK = false;
- }
- } catch (PGPException e) {
- Toast.makeText(activity, R.string.wrong_passphrase,
- Toast.LENGTH_SHORT).show();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- return;
- }
- } else {
- Toast.makeText(activity, R.string.error_could_not_extract_private_key,
- Toast.LENGTH_SHORT).show();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- return; // ran out of keys to try
+
+ // Early breakout if we are dealing with a symmetric key
+ if (secretRing == null) {
+ PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase);
+ // also return passphrase back to activity
+ Bundle data = new Bundle();
+ data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
+ sendMessageToHandler(MESSAGE_OKAY, data);
+ return;
+ }
+
+ WrappedSecretKey unlockedSecretKey = null;
+
+ for(WrappedSecretKey clickSecretKey : secretRing.iterator()) {
+ try {
+ boolean unlocked = clickSecretKey.unlock(passphrase);
+ if (unlocked) {
+ unlockedSecretKey = clickSecretKey;
+ break;
}
+ } catch (PgpGeneralException e) {
+ Toast.makeText(activity, R.string.error_could_not_extract_private_key,
+ Toast.LENGTH_SHORT).show();
+
+ sendMessageToHandler(MESSAGE_CANCEL);
+ return; // ran out of keys to try
}
- keyId = secretKey.getKeyID();
- } else {
- keyId = Constants.key.symmetric;
}
+ // Means we got an exception every time
+ if (unlockedSecretKey == null) {
+ Toast.makeText(activity, R.string.wrong_passphrase,
+ Toast.LENGTH_SHORT).show();
+
+ sendMessageToHandler(MESSAGE_CANCEL);
+ return;
+ }
+
+ long masterKeyId = secretRing.getMasterKeyId();
+
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
- PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
- if (!keyOK && clickSecretKey.getKeyID() != keyId) {
- PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
- passphrase);
+ PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase);
+ if (unlockedSecretKey.getKeyId() != masterKeyId) {
+ PassphraseCacheService.addCachedPassphrase(
+ activity, unlockedSecretKey.getKeyId(), passphrase);
}
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
-
sendMessageToHandler(MESSAGE_OKAY, data);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 1628c9e95..40fe7665c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -38,22 +38,18 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
-import java.util.Vector;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
- private PGPSecretKey mKey;
+ private UncachedSecretKey mKey;
private EditorListener mEditorListener = null;
@@ -208,7 +204,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
}
- public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
+ public void setValue(UncachedSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key;
mIsMasterKey = isMasterKey;
@@ -216,13 +212,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mDeleteButton.setVisibility(View.INVISIBLE);
}
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key));
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key.getAlgorithm()));
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyId());
mKeyId.setText(keyIdStr);
- Vector<Choice> choices = new Vector<Choice>();
- boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
- boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
+ boolean isElGamalKey = (key.isElGamalEncrypt());
+ boolean isDSAKey = (key.isDSA());
if (isElGamalKey) {
mChkSign.setVisibility(View.INVISIBLE);
TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
@@ -248,38 +243,45 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mIsNewKey = isNewKey;
if (isNewKey) {
mUsage = usage;
- mChkCertify.setChecked((usage & KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER);
- mChkSign.setChecked((usage & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA);
- mChkEncrypt.setChecked(((usage & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) ||
- ((usage & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE));
- mChkAuthenticate.setChecked((usage & KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION);
+ mChkCertify.setChecked(
+ (usage & UncachedSecretKey.CERTIFY_OTHER) == UncachedSecretKey.CERTIFY_OTHER);
+ mChkSign.setChecked(
+ (usage & UncachedSecretKey.SIGN_DATA) == UncachedSecretKey.SIGN_DATA);
+ mChkEncrypt.setChecked(
+ ((usage & UncachedSecretKey.ENCRYPT_COMMS) == UncachedSecretKey.ENCRYPT_COMMS) ||
+ ((usage & UncachedSecretKey.ENCRYPT_STORAGE) == UncachedSecretKey.ENCRYPT_STORAGE));
+ mChkAuthenticate.setChecked(
+ (usage & UncachedSecretKey.AUTHENTICATION) == UncachedSecretKey.AUTHENTICATION);
} else {
- mUsage = PgpKeyHelper.getKeyUsage(key);
+ mUsage = key.getKeyUsage();
mOriginalUsage = mUsage;
if (key.isMasterKey()) {
- mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key));
+ mChkCertify.setChecked(key.canCertify());
}
- mChkSign.setChecked(PgpKeyHelper.isSigningKey(key));
- mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key));
- mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key));
+ mChkSign.setChecked(key.canSign());
+ mChkEncrypt.setChecked(key.canEncrypt());
+ mChkAuthenticate.setChecked(key.canAuthenticate());
}
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- cal.setTime(PgpKeyHelper.getCreationDate(key));
- setCreatedDate(cal);
- cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- Date expiryDate = PgpKeyHelper.getExpiryDate(key);
+ {
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ cal.setTime(key.getCreationTime());
+ setCreatedDate(cal);
+ }
+
+ Date expiryDate = key.getExpiryTime();
if (expiryDate == null) {
setExpiryDate(null);
} else {
- cal.setTime(PgpKeyHelper.getExpiryDate(key));
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ cal.setTime(expiryDate);
setExpiryDate(cal);
mOriginalExpiryDate = cal;
}
}
- public PGPSecretKey getValue() {
+ public UncachedSecretKey getValue() {
return mKey;
}
@@ -320,16 +322,16 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public int getUsage() {
- mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) |
- (mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0);
- mUsage = (mUsage & ~KeyFlags.SIGN_DATA) |
- (mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0);
- mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) |
- (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0);
- mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) |
- (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0);
- mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) |
- (mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.CERTIFY_OTHER) |
+ (mChkCertify.isChecked() ? UncachedSecretKey.CERTIFY_OTHER : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.SIGN_DATA) |
+ (mChkSign.isChecked() ? UncachedSecretKey.SIGN_DATA : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_COMMS) |
+ (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_COMMS : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_STORAGE) |
+ (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_STORAGE : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.AUTHENTICATION) |
+ (mChkAuthenticate.isChecked() ? UncachedSecretKey.AUTHENTICATION : 0);
return mUsage;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index a7719012a..3e8e18ce5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -35,10 +35,9 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.openpgp.PGPKeyFlags;
-import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@@ -63,7 +62,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
private int mNewKeySize;
private boolean mOldItemDeleted = false;
private ArrayList<String> mDeletedIDs = new ArrayList<String>();
- private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
+ private ArrayList<UncachedSecretKey> mDeletedKeys = new ArrayList<UncachedSecretKey>();
private boolean mCanBeEdited = true;
private ActionBarActivity mActivity;
@@ -227,7 +226,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
return mDeletedIDs;
}
- public ArrayList<PGPSecretKey> getDeletedKeys() {
+ public ArrayList<UncachedSecretKey> getDeletedKeys() {
return mDeletedKeys;
}
@@ -325,7 +324,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
this.updateEditorsVisible();
}
- public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) {
+ public void setKeys(Vector<UncachedSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != TYPE_KEY) {
return;
}
@@ -358,9 +357,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
String passphrase;
if (mEditors.getChildCount() > 0) {
- PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
+ UncachedSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
passphrase = PassphraseCacheService
- .getCachedPassphrase(mActivity, masterKey.getKeyID());
+ .getCachedPassphrase(mActivity, masterKey.getKeyId());
isMasterKey = false;
} else {
passphrase = "";
@@ -395,7 +394,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get new key from data bundle returned from service
Bundle data = message.getData();
- PGPSecretKey newKey = (PGPSecretKey) PgpConversionHelper
+ UncachedSecretKey newKey = PgpConversionHelper
.BytesToPGPSecretKey(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
addGeneratedKeyToView(newKey);
@@ -413,14 +412,14 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mActivity.startService(intent);
}
- private void addGeneratedKeyToView(PGPSecretKey newKey) {
+ private void addGeneratedKeyToView(UncachedSecretKey newKey) {
// add view with new key
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false);
view.setEditorListener(SectionView.this);
int usage = 0;
if (mEditors.getChildCount() == 0) {
- usage = PGPKeyFlags.CAN_CERTIFY;
+ usage = UncachedSecretKey.CERTIFY_OTHER;
}
view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view);
diff --git a/OpenKeychain/src/main/res/raw-fa-rIR/help_about.html b/OpenKeychain/src/main/res/raw-ar/help_about.html
index ae7e16aae..ab3c19375 100644
--- a/OpenKeychain/src/main/res/raw-fa-rIR/help_about.html
+++ b/OpenKeychain/src/main/res/raw-ar/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-cs-rCZ/help_changelog.html b/OpenKeychain/src/main/res/raw-ar/help_changelog.html
index ebada67f9..ebada67f9 100644
--- a/OpenKeychain/src/main/res/raw-cs-rCZ/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-ar/help_changelog.html
diff --git a/OpenKeychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-ar/help_nfc_beam.html
index 88492731c..88492731c 100644
--- a/OpenKeychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html
+++ b/OpenKeychain/src/main/res/raw-ar/help_nfc_beam.html
diff --git a/OpenKeychain/src/main/res/raw-nl-rNL/help_start.html b/OpenKeychain/src/main/res/raw-ar/help_start.html
index 6ded872ea..3a681f8fa 100644
--- a/OpenKeychain/src/main/res/raw-nl-rNL/help_start.html
+++ b/OpenKeychain/src/main/res/raw-ar/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>I found a bug in OpenKeychain!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-ar/help_wot.html b/OpenKeychain/src/main/res/raw-ar/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-ar/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-ar/nfc_beam_share.html
index 083e055c7..083e055c7 100644
--- a/OpenKeychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html
+++ b/OpenKeychain/src/main/res/raw-ar/nfc_beam_share.html
diff --git a/OpenKeychain/src/main/res/raw-nl-rNL/help_about.html b/OpenKeychain/src/main/res/raw-cs/help_about.html
index ae7e16aae..ab3c19375 100644
--- a/OpenKeychain/src/main/res/raw-nl-rNL/help_about.html
+++ b/OpenKeychain/src/main/res/raw-cs/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-fa-rIR/help_changelog.html b/OpenKeychain/src/main/res/raw-cs/help_changelog.html
index ebada67f9..ebada67f9 100644
--- a/OpenKeychain/src/main/res/raw-fa-rIR/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-cs/help_changelog.html
diff --git a/OpenKeychain/src/main/res/raw-fa-rIR/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html
index 88492731c..88492731c 100644
--- a/OpenKeychain/src/main/res/raw-fa-rIR/help_nfc_beam.html
+++ b/OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html
diff --git a/OpenKeychain/src/main/res/raw-cs-rCZ/help_start.html b/OpenKeychain/src/main/res/raw-cs/help_start.html
index 6ded872ea..3a681f8fa 100644
--- a/OpenKeychain/src/main/res/raw-cs-rCZ/help_start.html
+++ b/OpenKeychain/src/main/res/raw-cs/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>I found a bug in OpenKeychain!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-cs/help_wot.html b/OpenKeychain/src/main/res/raw-cs/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-cs/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-fa-rIR/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html
index 083e055c7..083e055c7 100644
--- a/OpenKeychain/src/main/res/raw-fa-rIR/nfc_beam_share.html
+++ b/OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html
diff --git a/OpenKeychain/src/main/res/raw-de/help_about.html b/OpenKeychain/src/main/res/raw-de/help_about.html
index c40267e73..8eb033e9f 100644
--- a/OpenKeychain/src/main/res/raw-de/help_about.html
+++ b/OpenKeychain/src/main/res/raw-de/help_about.html
@@ -19,11 +19,12 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Entwickler APG 1.x</h2>
<ul>
<li>Thialfihar (Hauptentwickler)</li>
-<li>'Senecaso' (QR-Code, Schlüssel signtieren, Schlüssel hochladen)</li>
+<li>'Senecaso' (QR-Code, Schlüssel signieren, Schlüssel hochladen)</li>
<li>Markus Doits</li>
</ul>
<h2>Bibliotheken</h2>
diff --git a/OpenKeychain/src/main/res/raw-de/help_changelog.html b/OpenKeychain/src/main/res/raw-de/help_changelog.html
index 4f8206bf7..9ef142382 100644
--- a/OpenKeychain/src/main/res/raw-de/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-de/help_changelog.html
@@ -3,11 +3,11 @@
<body>
<h2>2.7</h2>
<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Lila! (Dominik, Vincent)</li>
+<li>Neues Schlüsselansicht Design (Dominik, Vincent)</li>
+<li>Neue flache Android Buttons (Dominik, Vincent)</li>
+<li>API Fehler behoben (Dominik)</li>
+<li>keybase.io importiert (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
@@ -15,51 +15,51 @@
</ul>
<h2>2.6</h2>
<ul>
-<li>key certifications (thanks to Vincent Breitmoser)</li>
-<li>support for GnuPG partial secret keys (thanks to Vincent Breitmoser)</li>
-<li>new design for signature verification</li>
-<li>custom key length (thanks to Greg Witczak)</li>
-<li>fix share-functionality from other apps</li>
+<li>Schlüsselzertifikation (Dank an Vincent Breitmoser)</li>
+<li>Unterstützung für GnuPG teilweisegeheime Schlüssel (Dank an Vincent Breitmoser)</li>
+<li>Neues Design für Signaturverifikation</li>
+<li>Nutzerdefinierte Schlüssellänge (Dank an Greg Witczak)</li>
+<li>Fehler behoben bei der Teilen-Funktion von anderen Apps</li>
</ul>
<h2>2.5</h2>
<ul>
-<li>fix decryption of symmetric pgp messages/files</li>
-<li>refactored edit key screen (thanks to Ash Hughes)</li>
-<li>new modern design for encrypt/decrypt screens</li>
-<li>OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)</li>
+<li>behoben: entschlüsseln von symetrischen pgp Nachrichten/Dateien</li>
+<li>umgestalteter Schlüssel bearbeiten Bildschirm (Dank an Ash Hughes)</li>
+<li>neues modernes Design für verschlüsselung/entschlüsselungs Bildschirme</li>
+<li>OpenPGP API Version 3 (mehrfache api accounts, interne fehlerbehebungen, schlüssel suche)</li>
</ul>
<h2>2.4</h2>
-<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
-Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+<p>Danke an alle Bewerber beim Google Summer of Code 2014, die diese Version funktionsreich und fehlerfrei gemacht haben.
+Neben mehreren kleinen Updates sind eine beachtliche Anzahl von Updates von den folgenden Personen gemacht worden (in alphabetischer Reihenfolge):
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
<ul>
-<li>new unified key list</li>
-<li>colorized key fingerprint</li>
-<li>support for keyserver ports</li>
-<li>deactivate possibility to generate weak keys</li>
-<li>much more internal work on the API</li>
-<li>certify user ids</li>
-<li>keyserver query based on machine-readable output</li>
+<li>neue vereinheitlichte Schlüssel liste.</li>
+<li>eingefärbte Schlüssel fingerprints</li>
+<li>Unterstützung für Schlüsselserver </li>
+<li>deaktiviert die Möglichkeit schwache Schlüssel zu generieren.</li>
+<li>viel mehr interne Arbeit an der API</li>
+<li>zertifizieren von Benutzer IDs</li>
+<li>Schlüsselserver Anfragen basieren auf Maschinenlesbaren Ausgaben.</li>
<li>lock navigation drawer on tablets</li>
<li>suggestions for emails on creation of keys</li>
-<li>search in public key lists</li>
-<li>and much more improvements and fixes…</li>
+<li>suchen in öffentlichen Schlüssellisten</li>
+<li>und viele weitere Verbesserungen und Fehlerbehebungen...</li>
</ul>
<h2>2.3.1</h2>
<ul>
-<li>hotfix for crash when upgrading from old versions</li>
+<li>schnelle Fehlerbehebung für Abstürze sobald man von einer alten Version geupdatet hat.</li>
</ul>
<h2>2.3</h2>
<ul>
-<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
-<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
-<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
-<li>querying keyservers directly from the import screen</li>
-<li>fix layout and dialog style on Android 2.2-3.0</li>
+<li>entfernung von unnötigen exporten von öffentlichen Schlüsseln, wenn man den geheimen Schlüssel exportiert. (Dank an Ash Hughes)</li>
+<li>Fehlerbehebung: Einstellen des Ablaufdatums von Schlüsseln (Dank an Ash Hughes)</li>
+<li>mehr interne Fehlerbehebungen wenn man Schlüssel bearbeitet (Dank an Ash Hughes)</li>
+<li>Suchzugriff auf die Schlüsselserver direkt vom Importieren Bildschirm</li>
+<li>Fehlerbehebung beim Layout und Dialogstil auf Android 2.2-3.0</li>
<li>Absturz bei leeren Nutzer IDs behoben </li>
-<li>fix crash and empty lists when coming back from signing screen</li>
-<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
-<li>fix upload of key from signing screen</li>
+<li>Fehlerbehebung von Abstürzen und leeren Listen, wenn man vom signieren Bildschirm zurückkehrt.</li>
+<li>Bouncy Castle (Kryptographie Bibliothek) upgedatet von 1.47 auf 1.50 und vom Quellcode kompiliert.</li>
+<li>Fehlerbehebung vom hochladen von Schlüsseln vom signieren Bildschirm.</li>
</ul>
<h2>2.2</h2>
<ul>
@@ -67,15 +67,15 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
<li>Neus Design für die Liste der öffentlichen Schlüssel</li>
<li>Neue Ansicht für öffentliche Schlüssel</li>
<li>Fehler beim Schlüsselimport behoben</li>
-<li>key cross-certification (thanks to Ash Hughes)</li>
-<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
+<li>Schlüssel Cross-Zertifizierung (Dank an Ash Hughes)</li>
+<li>richtiges händling von UTF-8 Passwörtern (Danke an Ash Hughes)</li>
<li>Erste Version mit neuen Sprachen (Danke an die Mitwirkenden bei Transifex)</li>
-<li>sharing of keys via QR Codes fixed and improved</li>
-<li>package signature verification for API</li>
+<li>teilen von schlüsseln per QR-Codes behoben und verbessert</li>
+<li>paket signatur verifizierung für die API</li>
</ul>
<h2>2.1.1</h2>
<ul>
-<li>API Updates, preparation for K-9 Mail integration</li>
+<li>API Updates, vorbereitung für die K-9 Mail integration.</li>
</ul>
<h2>2.1</h2>
<ul>
@@ -103,7 +103,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
</ul>
<h2>1.0.7</h2>
<ul>
-<li>fixed problem with signature verification of texts with trailing newline</li>
+<li>Fehlerbehebung bei Problemen mit der Signatur verifizierung von Texten mit folgender neuer Zeile.</li>
<li>weitere Optionen für die Time-to-live des Passphrasencaches (20, 40, 60 mins)</li>
</ul>
<h2>1.0.6</h2>
@@ -126,7 +126,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
</ul>
<h2>1.0.4</h2>
<ul>
-<li>fixed another crash caused by some SDK bug with query builder</li>
+<li>Einen anderen crash behoben, verursacht von irgendeinen SDK bug mit dem query builder.</li>
</ul>
<h2>1.0.3</h2>
<ul>
@@ -135,13 +135,13 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
<h2>1.0.2</h2>
<ul>
<li>Filterbare Schlüsselliste</li>
-<li>smarter pre-selection of encryption keys</li>
-<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
-<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
+<li>leichtere vorauswahl von Verschlüsselungsschlüsseln</li>
+<li>neues Intent händling für VIEW und SEND, erlaubt es Dateien zu ver-/entschlüsseln außerhalb des Dateimanagers.</li>
+<li>Fehlerbehebungen und Extras (Schlüssel vorauswahl) für K-9 Mail, neue Beta Versionen verfügbar.</li>
</ul>
<h2>1.0.1</h2>
<ul>
-<li>GMail account listing was broken in 1.0.0, fixed again</li>
+<li>GMail Account auflistung war nicht in Ordnung in 1.0.0, wieder behoben.</li>
</ul>
<h2>1.0.0</h2>
<ul>
@@ -149,7 +149,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
<li>Unterstützung von mehr Filemanagern (einschließlich ASTRO)</li>
<li>Slowenische Übersetzung</li>
<li>Neue Datenbank, viel schneller, weniger Speicherbedarf</li>
-<li>defined Intents and content provider for other apps</li>
+<li>definierte intents und content provider für andere Apps</li>
<li>Fehlerbehebungen</li>
</ul>
</body>
diff --git a/OpenKeychain/src/main/res/raw-de/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-de/help_nfc_beam.html
index 6fb3d6dd1..0526783cd 100644
--- a/OpenKeychain/src/main/res/raw-de/help_nfc_beam.html
+++ b/OpenKeychain/src/main/res/raw-de/help_nfc_beam.html
@@ -1,12 +1,12 @@
<html>
<head></head>
<body>
-<h2>Wie werden Schlüssel empfangen</h2>
+<h2>Wie kann man Schlüssel empfangen</h2>
<ol>
-<li>Gehen Sie zu Ihren Kontakten und öffnen Sie den Kontakt, den Sie teilen wollen.</li>
-<li>Halten sie die zwei Geräte Rückseitig aneinander (sie müssen sich fast berühren) und sie werden ein Vibrieren fühlen.</li>
-<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
-<li>Tippen sie die Karte an und der Inhalt wird dann auf Ihr Gerät geladen.</li>
+<li>Gehen Sie zu den Kontakten ihres Bekannten und öffnen Sie den Kontakt, den Sie teilen möchten.</li>
+<li>Halten Sie die zwei Geräte rückseitig aneinander (sodass sie sich fast berühren) und es wird vibrieren</li>
+<li>Nachdem es vibriert sehen sie wie sich der Inhalt des Gerätes ihres Bekannten von einer Warp-Geschwindigkeit Animation umgeben wird</li>
+<li>Wenn sie die Karte antippen, wird der Inhalt auf ihr Gerät übertragen</li>
</ol>
</body>
</html>
diff --git a/OpenKeychain/src/main/res/raw-de/help_start.html b/OpenKeychain/src/main/res/raw-de/help_start.html
new file mode 100644
index 000000000..1538fbce4
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-de/help_start.html
@@ -0,0 +1,22 @@
+<html>
+<head></head>
+<body>
+<h2>Los gehts</h2>
+<p>Zuerst benötigen Sie einen geheimen privaten Schlüssel. Erstellen Sie einen Schlüssel über den Eintrag &quot;Schlüssel&quot; im Menu oder importieren Sie bestehende private Schlüssel. Anschließend können Sie die Schlüssel ihrer Freunde runterladen oder Sie über QR-Codes oder NFC austauschen.</p>
+
+<p>Es wird empfohlen, dass Sie den <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> für ein verbessertes Dateihandling installieren, sowie den <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> installieren um erstellte QR-Codes zu scannen. Die Links führen entweder zu Google Play oder zu F-Droid zur Installation.</p>
+
+<h2>Anwendungen</h2>
+<p>Etliche Anwendungen unterstützen OpenKeychain um private Kommunikation zu verschlüsseln/signieren:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychainUnterstützung in der aktuellenen <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha Version</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App zum senden von PGP-signierten Anfragen an einen Server um etwas zu öffnen oder zu schliessen, z.B. eine Tür.</p>
+
+<h2>Ich habe einen Fehler in der OpenKeychain gefunden!</h2>
+<p>Bitte berichten Sie Bugs mithilfe des <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">Issue Trackers der OpenKeychain</a>.</p>
+
+<h2>Unterstützen</h2>
+<p>Wenn Sie uns bei der Entwickelung von OpenKeychain durch das Beisteuern von Code helfen wollt, <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">schaut euch unsere kurze Anleitung auf Github an</a>.</p>
+
+<h2>Übersetzungen</h2>
+<p>Hilf mit OpenKeychain zu übersetzten! Jeder kann auf <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain auf Transifex</a> mitmachen.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-de/help_wot.html b/OpenKeychain/src/main/res/raw-de/help_wot.html
new file mode 100644
index 000000000..ad33ddbb1
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-de/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>Das Web of Trust beschreibt den Teil von PGP der sich mit der Erstellung und dem Verwalten von Zertifikaten beschäftigt. Nutzer können so im Auge behalten zu wem ein bestimmter Public Key gehört, sowie diese Information teilen: Um die Privatsphäre von verschlüsselter Kommunikation zu gewährleisten ist es essentiell zu wissen ob der Public Key den man zum Verschlüsseln nutzt zu der Person gehört, die man erwartet.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>Es gibt nur eine rudimentäre Unterstützung für das Web of Trust in OpenKeychain. Am Web of Trust System wird kontinuierlich gearbeitet und es wird in den kommenden Version überarbeitet.</p>
+
+<h2>Vertrauens-Modell</h2>
+<p>Die Bewertung des Vertrauens basiert auf der Grundannahme, dass alle Schlüssel zu denen ein privater Schlüssel vorhanden ist, vertrauenswürdig sind. Öffentliche Schlüssel welche mindestens eine User ID beinhalten, die von einem Trusted Key verifiziert wurden, werden mit einem grünen Punkt in der Schlüsselliste gekennzeichnet. Es ist (noch) nicht möglich bestimmte Trust Level für Zertifikate anderer bekannter öffentlicher Schlüssel festzulegen.</p>
+
+<h2>Schlüssel beglaubigen</h2>
+<p>Die Unterstützung für die Zertifizierung von Schlüsseln ist vorhanden. Nutzer IDs können separat zertifiziert werden. Es ist jedoch sowohl noch nicht möglich das Vertrauenslevel festzulegen, als auch lokale oder andere spezielle Zertifikate zu erstellen.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-de/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-de/nfc_beam_share.html
index 083e055c7..8a2e1880e 100644
--- a/OpenKeychain/src/main/res/raw-de/nfc_beam_share.html
+++ b/OpenKeychain/src/main/res/raw-de/nfc_beam_share.html
@@ -2,10 +2,10 @@
<head></head>
<body>
<ol>
-<li>Make sure that NFC is turned on in Settings &gt; More &gt; NFC and make sure that Android Beam is also on in the same section.</li>
-<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
-<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
-<li>Tap the card and the content will then load on the other person’s device.</li>
+<li>Stellen Sie sicher, dass NFC und Android Beam in den Einstellungen unter &gt; Mehr &gt; NFC eingeschaltet sind</li>
+<li>Halten sie die zwei Geräte rückseitig aneinander (sodass sie sich fast berühren) und es wird vibrieren</li>
+<li>Nachdem es vibriert sehen sie wie der Inhalt ihres Gerätes von einer Warp-Geschwindigkeit Animation umgeben wird</li>
+<li>Drücken Sie auf die Karte und der Inhalt wird auf das Gerät des Anderen geladen.</li>
</ol>
</body>
</html>
diff --git a/OpenKeychain/src/main/res/raw-el/help_about.html b/OpenKeychain/src/main/res/raw-el/help_about.html
index ae7e16aae..ab3c19375 100644
--- a/OpenKeychain/src/main/res/raw-el/help_about.html
+++ b/OpenKeychain/src/main/res/raw-el/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-el/help_start.html b/OpenKeychain/src/main/res/raw-el/help_start.html
index 6ded872ea..3a681f8fa 100644
--- a/OpenKeychain/src/main/res/raw-el/help_start.html
+++ b/OpenKeychain/src/main/res/raw-el/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>I found a bug in OpenKeychain!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-el/help_wot.html b/OpenKeychain/src/main/res/raw-el/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-el/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-es/help_about.html b/OpenKeychain/src/main/res/raw-es/help_about.html
index 7a4f61127..a6067c8b9 100644
--- a/OpenKeychain/src/main/res/raw-es/help_about.html
+++ b/OpenKeychain/src/main/res/raw-es/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Desarrolladores de APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-es/help_changelog.html b/OpenKeychain/src/main/res/raw-es/help_changelog.html
index b86d29adb..0cd3773a2 100644
--- a/OpenKeychain/src/main/res/raw-es/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-es/help_changelog.html
@@ -4,10 +4,10 @@
<h2>2.7</h2>
<ul>
<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Diseño de vista de nueva clave (Dominik, Vincent)</li>
+<li>Nuevos botones de Android planos (Dominik, Vincent)</li>
+<li>Reparaciones de la API (Dominik)</li>
+<li>Importación de Keybase.io (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-es/help_start.html b/OpenKeychain/src/main/res/raw-es/help_start.html
index d9eaa90b5..5fcda44b4 100644
--- a/OpenKeychain/src/main/res/raw-es/help_start.html
+++ b/OpenKeychain/src/main/res/raw-es/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Primeros pasos</h2>
-<p>Primero necesita una clave privada (secreta) personal. Cree una mediante las opciones de menú en "Claves", o importe claves privadas existentes. En adelante puede descargar las claves de sus amigos, o intercambiarlas mediante códigos QR o vía NFC.</p>
+<p>Primero necesita una clave privada (secreta) personal. Cree una mediante las opciones de menú en &quot;Claves&quot;, o importe claves privadas existentes. En adelante puede descargar las claves de sus amigos, o intercambiarlas mediante códigos QR o vía NFC.</p>
<p>Es recomendable que instales <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> para una mejor selección de archivos y <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> para escanear los códigos QR generados. Pulsando en los enlaces se abrirá Google Play o F-Droid.</p>
<h2>Aplicaciones</h2>
-<p>Varias aplicaciones soportan OpenKeychain para cifrar/firmar sus comunicaciones privadas:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversaciones</a>: Cliente Jabber/XMPP</p>
-<p><img src="apps_k9">K-9 Mail: ¡Soporte para OpenKeychain disponible en la actual <a href="https://github.com/k9mail/k-9/releases/tag/4.904">versión alfa</a>!</p>
+<p>Varias aplicaciones soportan OpenKeychain para cifrar/firmar sus comunicaciones privadas:<br><img src="apps_k9"><br>K-9 Mail: Soporte para OpenKeychain disponible en la actual ¡<a href="https://github.com/k9mail/k-9/releases/tag/4.904">versión alfa</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversaciones</a>: Cliente Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: Aplicación para enviar una petición firmada-con-PGP a un servidor para abrir o cerrar algo, ej: una puerta</p>
<h2>¡He encontrado un bug en OpenKeychain!</h2>
<p>Por favor, informa de errores usando el <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">seguimiento de incidencias de OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-es/help_wot.html b/OpenKeychain/src/main/res/raw-es/help_wot.html
new file mode 100644
index 000000000..60a2abb02
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-es/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>El Web of Trust (web de confianza) describe la parte de PGP que trata con la creación y catalogación de certificaciones. Proporciona mecanismos para ayudar al usuario a llevar un seguimiento de a quién pertenece una clave pública, y comparte esta información con otros. Para asegurar la privacidad de las comunicaciones cifradas, es esencial saber que la clave pública que usted cifra pertenece a la misma persona que usted cree.</p>
+
+<h2>Soporte en OpenKeychain</h2>
+<p>Sólo hay soporte básico para Web of Trust (web de confianza) en OpenKeychain. Este es un duro trabajo que tenemos en marcha sujeto a cambios en versiones posteriores.</p>
+
+<h2>Modelo de confianza</h2>
+<p>La evaluación de la confianza está basada en la simple asunción de que todas las claves que tienen disponibles claves privadas (secretas) son de confianza. Las claves públicas que contienen al menos una identificación de usuario certificada por una clave de confianza serán marcadas con un punto verde en los listados de claves. No es posible (aún) especificar niveles de confianza para certificados de otras claves públicas conocidas.</p>
+
+<h2>Certificando claves</h2>
+<p>Está disponible el soporte para certificación de clave, y las identificaciones de usuario pueden ser certificadas individualmente. No es posible aún especificar el nivel de confianza o crear certificados locales y otros tipos especiales de certificados.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-et/help_about.html b/OpenKeychain/src/main/res/raw-et/help_about.html
index ae7e16aae..ab3c19375 100644
--- a/OpenKeychain/src/main/res/raw-et/help_about.html
+++ b/OpenKeychain/src/main/res/raw-et/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-et/help_start.html b/OpenKeychain/src/main/res/raw-et/help_start.html
index 6ded872ea..3a681f8fa 100644
--- a/OpenKeychain/src/main/res/raw-et/help_start.html
+++ b/OpenKeychain/src/main/res/raw-et/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>I found a bug in OpenKeychain!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-et/help_wot.html b/OpenKeychain/src/main/res/raw-et/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-et/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-fa-rIR/help_start.html b/OpenKeychain/src/main/res/raw-fa-rIR/help_start.html
deleted file mode 100644
index e8c04c7ae..000000000
--- a/OpenKeychain/src/main/res/raw-fa-rIR/help_start.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<html>
-<head></head>
-<body>
-<h2>شروع کار</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
-
-<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
-
-<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
-
-<h2>I found a bug in OpenKeychain!</h2>
-<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
-
-<h2>هم بخشی کردن</h2>
-<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
-
-<h2>ترجمه ها</h2>
-<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
-
-</body>
-</html>
diff --git a/OpenKeychain/src/main/res/raw-fr/help_about.html b/OpenKeychain/src/main/res/raw-fr/help_about.html
index 00370c77e..351bdc8f4 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_about.html
+++ b/OpenKeychain/src/main/res/raw-fr/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Les développeurs d'APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-fr/help_changelog.html b/OpenKeychain/src/main/res/raw-fr/help_changelog.html
index 3a8b958c8..2746881fb 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-fr/help_changelog.html
@@ -3,11 +3,11 @@
<body>
<h2>2.7</h2>
<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Violet ! (Dominik, Vincent)</li>
+<li>Nouvelle présentation de la visualisation des clefs (Dominik, Vincent)</li>
+<li>Nouveaux boutons Android plats (Dominik, Vincent)</li>
+<li>Correctifs de l'API (Dominik)</li>
+<li>Importation de Keybase.io (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-fr/help_start.html b/OpenKeychain/src/main/res/raw-fr/help_start.html
index 114bc3d81..01a4baa81 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_start.html
+++ b/OpenKeychain/src/main/res/raw-fr/help_start.html
@@ -4,12 +4,10 @@
<h2>Commencer</h2>
<p>Il vous faut d'abord une clef personnelle secrète. Créez-en une avec l'option du menu dans « Clefs » ou importer des clefs secrètes existantes. Ensuite vous pourrez télécharger les clefs de vos amis ou les échanger avec des codes QR ou par la NFC.</p>
-<p>Il vous est recommendé d'installer le <a href="market://details?id=org.openintents.filemanager">gestionnaire de fichiers OI</a> pour sa fonction améliorée de séléction des fichiers et le <a href="market://details?id=com.google.zxing.client.android">lecteur de codes à barres</a> pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.</p>
+<p>Il vous est recommandé d'installer le <a href="market://details?id=org.openintents.filemanager">gestionnaire de fichiers OI</a> pour sa fonction améliorée de sélection des fichiers et le <a href="market://details?id=com.google.zxing.client.android">lecteur de codes à barres</a> pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.</p>
<h2>Applications</h2>
-<p>Plusieurs applications prennent en charge OpenKeychain pour chiffrer/signer vos communications privées :</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a> : un client Jabber/XMPP</p>
-<p><img src="apps_k9">K-9 Mail : la prise en charge d'OpenKeychain est disponible dans la <a href="https://github.com/k9mail/k-9/releases/tag/4.904">version alpha</a> actuelle !</p>
+<p>Plusieurs applications prennent en charge OpenKeychain pour chiffrer/signer vos communications privées :<br><img src="apps_k9"><br>K-9 Mail : OpenKeychain est pris en charge dans la <a href="https://github.com/k9mail/k-9/releases/tag/4.904">version alpha</a>actuelle !<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a> : clilent Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a> : appli pour envoyer une demande signée par PGP à un serveur pour ouvrir ou fermer quelque chose, par exemple une porte</p>
<h2>J'ai trouvé un bogue dans OpenKeychain !</h2>
<p>Veuillez rapporter le bogue en utilisant le <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">gestionnaire de bogue d'OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-fr/help_wot.html b/OpenKeychain/src/main/res/raw-fr/help_wot.html
new file mode 100644
index 000000000..4162c9b1b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-fr/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Toile confiance</h2>
+<p>La toile de confiance décrit la partie de PGP qui s'occupe de la création et du suivi des certifications. Elle fournit des mécanismes pour aider l'utilisateur à suivre à qui appartient une clef publique, et partager cette information avec les autres. Pour assurer la confidentialité d'une communication chiffrée, il est essentiel de savoir que la clef publique vers laquelle vous chiffrez appartient à la personne à qui vous croyez qu'elle appartient.</p>
+
+<h2>Prise en charge dans OpenKeychain</h2>
+<p>OpenKeychain offre seulement une prise en charge de base de la toile de confiance. Un travail important est en cours et ceci pourrait changer dans les versions à venir.</p>
+
+<h2>Modèle de confiance</h2>
+<p>L'évaluation de la confiance est fondée sur la simple supposition que toutes les clefs ayant des clefs secrètes disponibles sont de confiance. Les clefs publiques contenant au moins un ID d'utilisateur certifié par une clef de confiance seront marquées par un point vert dans le listage des clefs. Il n'est pas (encore) possible de spécifier les niveaux de confiance pour les certificats d'autres clefs publiques inconnues.</p>
+
+<h2>Certifications des clefs</h2>
+<p>La prise en charge de la certification des clefs est disponible et les ID d'utilisateur peuvent être certifiées individuellement. Il n'est pas encore possible de spécifier le niveau de confiance ou de créer des certificats locaux et d'autres types spéciaux.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-it-rIT/help_about.html b/OpenKeychain/src/main/res/raw-it/help_about.html
index 8644d3fc6..4c8c0af07 100644
--- a/OpenKeychain/src/main/res/raw-it-rIT/help_about.html
+++ b/OpenKeychain/src/main/res/raw-it/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Sviluppatori APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-it-rIT/help_changelog.html b/OpenKeychain/src/main/res/raw-it/help_changelog.html
index b2d67ca80..ee6a56e15 100644
--- a/OpenKeychain/src/main/res/raw-it-rIT/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-it/help_changelog.html
@@ -3,11 +3,11 @@
<body>
<h2>2.7</h2>
<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Porpora! (Dominik, Vincent)</li>
+<li>Nuovo design della schermata chiavi (Dominik, Vincent)</li>
+<li>Nuovi pulsanti piatti Android (Dominik, Vincent)</li>
+<li>Correzioni API (Dominik)</li>
+<li>Importazione Keybase.io (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
@@ -31,7 +31,7 @@
<h2>2.4</h2>
<p>Grazie a tutti i partecipanti di Google Summer of Code 2014 che hanno reso questo rilascio ricco di caratteristiche e privo di bug!
Oltre a numerose piccole correzioni, un notevole numero di patch sono state fatte dalle seguenti persone (in ordine alfabetico):
-Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paolo Sarbinowski, Sreeram Boyapati, Vincent Breitmoser. </p>
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paolo Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
<ul>
<li>nuova lista chiave unificata</li>
<li>impronta chiave colorata</li>
diff --git a/OpenKeychain/src/main/res/raw-it-rIT/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-it/help_nfc_beam.html
index 6ff56194e..6ff56194e 100644
--- a/OpenKeychain/src/main/res/raw-it-rIT/help_nfc_beam.html
+++ b/OpenKeychain/src/main/res/raw-it/help_nfc_beam.html
diff --git a/OpenKeychain/src/main/res/raw-it-rIT/help_start.html b/OpenKeychain/src/main/res/raw-it/help_start.html
index 77866bc8c..138e0ece2 100644
--- a/OpenKeychain/src/main/res/raw-it-rIT/help_start.html
+++ b/OpenKeychain/src/main/res/raw-it/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Per iniziare</h2>
-<p>In primo luogo è necessaria una chiave segreta personale. Creane una tramite il menu opzioni sotto la voce "Chiavi" o importa chiavi segrete già esistenti. Successivamente, è possibile scaricare le chiavi dei vostri amici o scambiarle con i codici QR o NFC.</p>
+<p>In primo luogo è necessaria una chiave segreta personale. Creane una tramite il menu opzioni sotto la voce &quot;Chiavi&quot; o importa chiavi segrete già esistenti. Successivamente, è possibile scaricare le chiavi dei vostri amici o scambiarle con i codici QR o NFC.</p>
<p>Si raccomanda di installare <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> per una migliore selezione dei file e <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> per scansionare i codici QR. I collegamenti verranno aperti in Google Play Store o F-Droid per l'installazione.</p>
-<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<h2>Applicazioni</h2>
+<p>Diverse applicazioni supportano OpenKeychain per codificare/firmare le tue comunicazioni private:<br><img src="apps_k9"><br>K-9 Mail: Supporto per OpenKeychain disponibile nella attuale <a href="https://github.com/k9mail/k-9/releases/tag/4.904">versione alpha</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Client Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>App per inviare una richiesta firmata PGP a un server per aprire o chiudere qualcosa, ad esempio, una porta</a></p>
<h2>Ho trovato un bug in OpenKeychain!</h2>
<p>Per favore riporta i bug usando il <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker di OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-it/help_wot.html b/OpenKeychain/src/main/res/raw-it/help_wot.html
new file mode 100644
index 000000000..98e3aafea
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-it/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Rete di Fiducia</h2>
+<p>La Rete di Fiducia descrive la parte del PGP che si occupa di creazione e gestione delle certificazioni. Essa fornisce meccanismi per aiutare l'utente a tenere traccia di chi appartiene una chiave pubblica, e condividere queste informazioni con gli altri; Per garantire la privacy della comunicazione crittografata, è essenziale sapere che la chiave pubblica per crittografare appartiene alla persona che si pensa.</p>
+
+<h2>Supporto in OpenKeychain</h2>
+<p>Esiste solo un supporto di base per le Reti di Fiducia in OpenKeychain. Questo è un grosso lavoro in corso e soggetto a modifiche nei prossimi rilasci.</p>
+
+<h2>Modello di Fiducia</h2>
+<p>La valutazione di fiducia si basa sul semplice presupposto che tutte le chiavi che hanno chiavi segrete disponibili sono attendibili. Le chiavi pubbliche che contengono almeno un id utente certificato da una chiave di fiducia saranno contrassegnati con un punto verde negli elenchi principali. Non è (ancora) possibile specificare livelli di attendibilità per i certificati di altre chiavi pubbliche conosciute.</p>
+
+<h2>Chiavi di certificazione</h2>
+<p>Il supporto per la certificazione chiave è disponibile, e gli id utenti possono essere certificati singolarmente. Non è ancora possibile specificare il livello di fiducia o creare certificati locali e altro di tipo speciale.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-it-rIT/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-it/nfc_beam_share.html
index e75877efe..e75877efe 100644
--- a/OpenKeychain/src/main/res/raw-it-rIT/nfc_beam_share.html
+++ b/OpenKeychain/src/main/res/raw-it/nfc_beam_share.html
diff --git a/OpenKeychain/src/main/res/raw-ja/help_about.html b/OpenKeychain/src/main/res/raw-ja/help_about.html
index e60add867..a4670e103 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_about.html
+++ b/OpenKeychain/src/main/res/raw-ja/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>APG 1.xの開発者達</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-ja/help_changelog.html b/OpenKeychain/src/main/res/raw-ja/help_changelog.html
index 4bf40cbc3..0aae03ba6 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-ja/help_changelog.html
@@ -4,10 +4,10 @@
<h2>2.7</h2>
<ul>
<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>新しい鍵のビューのデザイン (Dominik, Vincent)</li>
+<li>新しいフラットな Android ボタン (Dominik, Vincent)</li>
+<li>API のフィックス (Dominik)</li>
+<li>Keybase.io からのインポート (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-ja/help_start.html b/OpenKeychain/src/main/res/raw-ja/help_start.html
index 502d4adb8..1e522cc74 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_start.html
+++ b/OpenKeychain/src/main/res/raw-ja/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>入門</h2>
-<p>最初にあなたの個人用秘密鍵が必要になります。オプションメニューの"鍵"で生成するか、既存の秘密鍵をインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。</p>
+<p>最初にあなたの個人用秘密鍵が必要になります。オプションメニューの&quot;鍵&quot;で生成するか、既存の秘密鍵をインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。</p>
<p>ファイルの選択を拡張するには<a href="market://details?id=org.openintents.filemanager">OI File Manager</a>、<a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a>を生成したQRコードのスキャンのため、それぞれのインストールを必要とします。 リンクをクリックして、Google Play Store上かF-Droidからインストールしてください。</p>
-<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<h2>アプリケーション</h2>
+<p>OpenKeychainはプライベートな通信での暗号化/署名をいくつかのアプリケーションでサポートしています:<br><img src="apps_k9"><br>K-9 Mail: 現在のOpenKeychain <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a> からサポートが有効です!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>やりとり</a>: Jabber/XMPP クライアント<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: このアプリはPGP署名されたリクエストをサーバに送り、なにかしらを開けたり閉じたりします、たとえばドアとか</p>
<h2>OpenKeychainでバグを見付けた!</h2>
<p><a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">OpenKeychainのIssueトラッカー</a>を使ってバグレポートを送ってください。</p>
diff --git a/OpenKeychain/src/main/res/raw-ja/help_wot.html b/OpenKeychain/src/main/res/raw-ja/help_wot.html
new file mode 100644
index 000000000..3675b6258
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-ja/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>信頼の輪</h2>
+<p>信頼の輪はPGPが証明の作成と維持する一面を説明します。ユーザが公開鍵が属する者の追跡を維持し、他のユーザーとその情報を共有する手助けになるメカニズムを提供します。暗号化通信のプライバシーを確保するためには、あなたが暗号化したいという相手の公開鍵が必要な要素となります</p>
+
+<h2>OpenKeychainでサポート</h2>
+<p>OpenKeychainで信頼の輪の基本のみサポートされます。とても重い作業を進めており今後やってくるリリースにおいて状態が変更されていきます。</p>
+
+<h2>信頼モデル</h2>
+<p>信頼の評価はシンプルな仮定に基づいています、それはすべての鍵は秘密鍵が信頼できるというものです。公開鍵は信頼された鍵で検証された最低1つのユーザIDを含んでおり、それは鍵リストにおいて、緑でマークされます。ただしそれは他の既知の公開鍵の信頼レベルを特別なものには(まだ)しません。</p>
+
+<h2>鍵の検証</h2>
+<p>鍵検証のサポートが提供されており、ユーザIDは個別に検証できます。ただしそれは他の既知の公開鍵の信頼レベルを特別なものにはまだしないか他の特別なタイプの証明です。</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-ko/help_about.html b/OpenKeychain/src/main/res/raw-ko/help_about.html
index ae7e16aae..ab3c19375 100644
--- a/OpenKeychain/src/main/res/raw-ko/help_about.html
+++ b/OpenKeychain/src/main/res/raw-ko/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-ko/help_start.html b/OpenKeychain/src/main/res/raw-ko/help_start.html
index 6ded872ea..3a681f8fa 100644
--- a/OpenKeychain/src/main/res/raw-ko/help_start.html
+++ b/OpenKeychain/src/main/res/raw-ko/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>I found a bug in OpenKeychain!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-ko/help_wot.html b/OpenKeychain/src/main/res/raw-ko/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-ko/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-nl-rNL/help_changelog.html b/OpenKeychain/src/main/res/raw-nl-rNL/help_changelog.html
deleted file mode 100644
index ebada67f9..000000000
--- a/OpenKeychain/src/main/res/raw-nl-rNL/help_changelog.html
+++ /dev/null
@@ -1,156 +0,0 @@
-<html>
-<head></head>
-<body>
-<h2>2.7</h2>
-<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
-</ul>
-<h2>2.6.1</h2>
-<ul>
-<li>some fixes for regression bugs</li>
-</ul>
-<h2>2.6</h2>
-<ul>
-<li>key certifications (thanks to Vincent Breitmoser)</li>
-<li>support for GnuPG partial secret keys (thanks to Vincent Breitmoser)</li>
-<li>new design for signature verification</li>
-<li>custom key length (thanks to Greg Witczak)</li>
-<li>fix share-functionality from other apps</li>
-</ul>
-<h2>2.5</h2>
-<ul>
-<li>fix decryption of symmetric pgp messages/files</li>
-<li>refactored edit key screen (thanks to Ash Hughes)</li>
-<li>new modern design for encrypt/decrypt screens</li>
-<li>OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)</li>
-</ul>
-<h2>2.4</h2>
-<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
-Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
-Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
-<ul>
-<li>new unified key list</li>
-<li>colorized key fingerprint</li>
-<li>support for keyserver ports</li>
-<li>deactivate possibility to generate weak keys</li>
-<li>much more internal work on the API</li>
-<li>certify user ids</li>
-<li>keyserver query based on machine-readable output</li>
-<li>lock navigation drawer on tablets</li>
-<li>suggestions for emails on creation of keys</li>
-<li>search in public key lists</li>
-<li>and much more improvements and fixes…</li>
-</ul>
-<h2>2.3.1</h2>
-<ul>
-<li>hotfix for crash when upgrading from old versions</li>
-</ul>
-<h2>2.3</h2>
-<ul>
-<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
-<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
-<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
-<li>querying keyservers directly from the import screen</li>
-<li>fix layout and dialog style on Android 2.2-3.0</li>
-<li>fix crash on keys with empty user ids</li>
-<li>fix crash and empty lists when coming back from signing screen</li>
-<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
-<li>fix upload of key from signing screen</li>
-</ul>
-<h2>2.2</h2>
-<ul>
-<li>new design with navigation drawer</li>
-<li>new public key list design</li>
-<li>new public key view</li>
-<li>bug fixes for importing of keys</li>
-<li>key cross-certification (thanks to Ash Hughes)</li>
-<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
-<li>first version with new languages (thanks to the contributors on Transifex)</li>
-<li>sharing of keys via QR Codes fixed and improved</li>
-<li>package signature verification for API</li>
-</ul>
-<h2>2.1.1</h2>
-<ul>
-<li>API Updates, preparation for K-9 Mail integration</li>
-</ul>
-<h2>2.1</h2>
-<ul>
-<li>lots of bug fixes</li>
-<li>new API for developers</li>
-<li>PRNG bug fix by Google</li>
-</ul>
-<h2>2.0</h2>
-<ul>
-<li>complete redesign</li>
-<li>share public keys via qr codes, nfc beam</li>
-<li>sign keys</li>
-<li>upload keys to server</li>
-<li>fixes import issues</li>
-<li>new AIDL API</li>
-</ul>
-<h2>1.0.8</h2>
-<ul>
-<li>basic keyserver support</li>
-<li>app2sd</li>
-<li>more choices for pass phrase cache: 1, 2, 4, 8, hours</li>
-<li>translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)</li>
-<li>bugfixes</li>
-<li>optimizations</li>
-</ul>
-<h2>1.0.7</h2>
-<ul>
-<li>fixed problem with signature verification of texts with trailing newline</li>
-<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
-</ul>
-<h2>1.0.6</h2>
-<ul>
-<li>account adding crash on Froyo fixed</li>
-<li>secure file deletion</li>
-<li>option to delete key file after import</li>
-<li>stream encryption/decryption (gallery, etc.)</li>
-<li>new options (language, force v3 signatures)</li>
-<li>interface changes</li>
-<li>bugfixes</li>
-</ul>
-<h2>1.0.5</h2>
-<ul>
-<li>German and Italian translation</li>
-<li>much smaller package, due to reduced BC sources</li>
-<li>new preferences GUI</li>
-<li>layout adjustment for localization</li>
-<li>signature bugfix</li>
-</ul>
-<h2>1.0.4</h2>
-<ul>
-<li>fixed another crash caused by some SDK bug with query builder</li>
-</ul>
-<h2>1.0.3</h2>
-<ul>
-<li>fixed crashes during encryption/signing and possibly key export</li>
-</ul>
-<h2>1.0.2</h2>
-<ul>
-<li>filterable key lists</li>
-<li>smarter pre-selection of encryption keys</li>
-<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
-<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
-</ul>
-<h2>1.0.1</h2>
-<ul>
-<li>GMail account listing was broken in 1.0.0, fixed again</li>
-</ul>
-<h2>1.0.0</h2>
-<ul>
-<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
-<li>support of more file managers (including ASTRO)</li>
-<li>Slovenian translation</li>
-<li>new database, much faster, less memory usage</li>
-<li>defined Intents and content provider for other apps</li>
-<li>bugfixes</li>
-</ul>
-</body>
-</html>
diff --git a/OpenKeychain/src/main/res/raw-nl-rNL/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-nl-rNL/help_nfc_beam.html
deleted file mode 100644
index 88492731c..000000000
--- a/OpenKeychain/src/main/res/raw-nl-rNL/help_nfc_beam.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<html>
-<head></head>
-<body>
-<h2>How to receive keys</h2>
-<ol>
-<li>Go to your partners contacts and open the contact you want to share.</li>
-<li>Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.</li>
-<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
-<li>Tap the card and the content will then load on the your device.</li>
-</ol>
-</body>
-</html>
diff --git a/OpenKeychain/src/main/res/raw-nl-rNL/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-nl-rNL/nfc_beam_share.html
deleted file mode 100644
index 083e055c7..000000000
--- a/OpenKeychain/src/main/res/raw-nl-rNL/nfc_beam_share.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<html>
-<head></head>
-<body>
-<ol>
-<li>Make sure that NFC is turned on in Settings &gt; More &gt; NFC and make sure that Android Beam is also on in the same section.</li>
-<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
-<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
-<li>Tap the card and the content will then load on the other person’s device.</li>
-</ol>
-</body>
-</html>
diff --git a/OpenKeychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenKeychain/src/main/res/raw-nl/help_about.html
index 99977f75d..0ac14d919 100644
--- a/OpenKeychain/src/main/res/raw-cs-rCZ/help_about.html
+++ b/OpenKeychain/src/main/res/raw-nl/help_about.html
@@ -2,12 +2,12 @@
<head></head>
<body>
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
-<p><a href="http://www.openkeychain.org">OpenKeychain</a> is an OpenPGP implementation for Android.</p>
-<p>Licence: GPLv3+</p>
+<p><a href="http://www.openkeychain.org">OpenKeychain</a> is een OpenPGP implementatie voor Android.</p>
+<p>Licentie: GPLv3+</p>
-<h2>Developers OpenKeychain</h2>
+<h2>Ontwikkelaars OpenKeychain</h2>
<ul>
-<li>Dominik Schürmann (Hlavní vývojář)</li>
+<li>Dominik Schürmann (hoofdontwikkelaar)</li>
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
@@ -19,31 +19,32 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
-<h2>Developers APG 1.x</h2>
+<h2>Ontwikkelaars APG 1.x</h2>
<ul>
-<li>Thialfihar (Lead developer)</li>
-<li>'Senecaso' (QRCode, sign key, upload key)</li>
+<li>Thialfihar (Hoofdontwikkelaar)</li>
+<li>'Senecaso' (QRCode, signeersleutel, uploadsleutel)</li>
<li>Markus Doits</li>
</ul>
-<h2>Libraries</h2>
+<h2>Bibliotheken</h2>
<ul>
<li>
-<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Biblitheek v4</a> (Apache Licentie v2)</li>
<li>
-<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Apache License v2)</li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Bibliotheek v7 'appcompat'</a> (Apache Licentie v2)</li>
<li>
-<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache License v2)</li>
+<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache Licentie v2)</li>
<li>
-<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT License)</li>
+<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT Licentie)</li>
<li>
-<a href="http://code.google.com/p/zxing/">ZXing</a> (Apache License v2)</li>
+<a href="http://code.google.com/p/zxing/">ZXing</a> (Apache Licentie v2)</li>
<li>
-<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
+<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 Licentie)</li>
<li>
-<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
+<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache Licentie v2)</li>
<li>
-<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
+<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache Licentie v2)</li>
</ul>
</body>
</html>
diff --git a/OpenKeychain/src/main/res/raw-nl/help_changelog.html b/OpenKeychain/src/main/res/raw-nl/help_changelog.html
new file mode 100644
index 000000000..38da3d57c
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nl/help_changelog.html
@@ -0,0 +1,156 @@
+<html>
+<head></head>
+<body>
+<h2>2.7</h2>
+<ul>
+<li>Paars! (Dominik, Vincent)</li>
+<li>Nieuw sleutel scherm design (Dominik, Vincent)</li>
+<li>Nieuwe platte Android toetsen (Dominik, Vincent)</li>
+<li>API fixes (Dominik)</li>
+<li>Keybase.io import (Tim Bray)</li>
+</ul>
+<h2>2.6.1</h2>
+<ul>
+<li>enige fixes voor regressie bugs</li>
+</ul>
+<h2>2.6</h2>
+<ul>
+<li>sleutel certificaties (dank aan Vincent Breitmoser)</li>
+<li>support voor GnuPG deel geheime sleutels (dank aan Vincent Breitmoser)</li>
+<li>nieuw design voor handtekeningsverificatie</li>
+<li>aangepaste sleutellengte (dank aan Greg Witczak)</li>
+<li>fix deel-functionaliteit van andere apps</li>
+</ul>
+<h2>2.5</h2>
+<ul>
+<li>fix decodering van symmetrische pgp berichten/bestanden</li>
+<li>bewerk sleutel scherm opnieuw gedaan (dank aan Ash Hughes)</li>
+<li>nieuw modern design voor codeer/decodeer schermen</li>
+<li>OpenPGP API versie 3 (meerdere api accounts, interne fixes, sleutel lookup)</li>
+</ul>
+<h2>2.4</h2>
+<p>Bedankt aan alle gegadigden van Google Summer of Code 2014 die deze release feature groot en zonder bugs maakten!
+Naast meerdere kleine patches zijn een redelijk aantal patches gemaakt door de volgende mensen (in alfabetische volgorde):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>nieuwe verenigde sleutellijst</li>
+<li>gekleurde sleutel vingerafdruk</li>
+<li>support voor sleutelserver ports</li>
+<li>deactiveer mogelijkheid om zwakke sleutels te genereren</li>
+<li>veel meer intern werk aan het API</li>
+<li>certificeer gebruiker ids</li>
+<li>sleutelserver opdracht gebaseerd op machine-leesbare output</li>
+<li>Versleutel navigatiemenu op tablets</li>
+<li>suggesties voor e-mails bij aanmaken van sleutels</li>
+<li>zoek in publieke sleutel lijsten</li>
+<li>en veel meer verbeteringen en fixes...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix voor crash bij het upgraden van oude versies</li>
+</ul>
+<h2>2.3</h2>
+<ul>
+<li>verwijder onnodige export van publieke sleutels bij het exporteren van de geheime sleutel (dank aan Ash Hughes)</li>
+<li>fix instellingen verloopdata op sleutels (dank aan Ash Hughes)</li>
+<li>meer interne fixes tijdens het bewerken van sleutels (dank aan Ash Hughes)</li>
+<li>sleutelservers direct van het importeerscherm zoeken</li>
+<li>fix layout en dialoog stijl op Android 2.2-3.0</li>
+<li>fix crash op sleutels met lege gebruiker id's</li>
+<li>fix crash en lege lijsten bij het terugkomen van het ondertekenscherm</li>
+<li>Bouncy Castle (cryptografie bibliotheek) bijgewerkt van 1.47 naar 1.50 en versie van bron</li>
+<li>fix uploaden van sleutel van ondertekenscherm</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>nieuw design met navigatiemenu</li>
+<li>nieuw publieke sleutel lijst design</li>
+<li>nieuw publieke sleutel uiterlijk</li>
+<li>bug fixes voor importeren van sleutels</li>
+<li>sleutel kruis-certificatie (dank aan Ash Hughes)</li>
+<li>behandelt UTF-8 wachtwoorden goed (dank aan Ash Hughes)</li>
+<li>eerste versie met nieuwe talen (dank aan de medewerkers bij Transifex)</li>
+<li>delen van sleutels via QR Codes gefixt en verbeterd</li>
+<li>pakket handtekening verificatie voor API</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>API Updates, voorbereiding voor K-9 Mail integratie</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>veel bugfixes</li>
+<li>nieuwe API voor ontwikkelaars</li>
+<li>PRNG bug fix door Google</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>complete herdesign</li>
+<li>deel publieke sleutels via qr codes, nfc beam</li>
+<li>sleutels ondertekenen</li>
+<li>upload sleutels naar server</li>
+<li>fixt importeerfouten</li>
+<li>nieuwe AIDL API</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>basis sleutelserver support</li>
+<li>app2sd</li>
+<li>meer keuzes voor wachtwoord cache: 1, 2, 4, 8, uren</li>
+<li>vertalingen: Noors (bedankt, Sander Danielsen), Chinees (bedankt, Zhang Fredrick)</li>
+<li>bugfixes</li>
+<li>optimalisaties</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>probleem met handtekeningverificatie van teksten met nieuwe trailing newline gefixt</li>
+<li>meer opties voor wachtwoord cache tijd te leven (20, 40, 60 minuten)</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>account toevoegen crash op Froyo gefixt</li>
+<li>veilige bestandsverwijdering</li>
+<li>optie om sleutel te verwijderen na importeren</li>
+<li>stream codering/decodering (gallery, etc.)</li>
+<li>nieuwe opties (taal, force v3 handtekeningen)</li>
+<li>interface veranderingen</li>
+<li>bugfixes</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>Duitse en Italiaanse vertaling</li>
+<li>veel kleiner pakket, door verminderde BC bronnen</li>
+<li>nieuwe voorkeuren GUI</li>
+<li>layout aanpassing voor plaatsbepaling</li>
+<li>handtekening bugfix</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>nog een crash gefixt veroorzaakt door een SDK bug met query builder</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>crashes tijdens codering/signering en mogelijk sleutel importeren gefixt</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>filterbare sleutellijsten</li>
+<li>slimmere voor-selectie van codeersleutels</li>
+<li>nieuwe Intent behandeling voor VIEW en SEND, maakt het mogelijk om bestanden te coderen/decoderen uit bestandsmanagers</li>
+<li>fixes en meer functies (sleutel voorselectie) voor K-9 Mail, nieuwe beta versie beschikbaar</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>GMail account lijsten was stuk in 1.0.0, weer gefixt</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>K-9 Mail integratie, APG ondersteunende beta versie van K-9 Mail</li>
+<li>support voor meer bestandsmanagers (inclusief ASTRO)</li>
+<li>Slovenische vertaling</li>
+<li>nieuwe database, veel sneller, minder geheugenverbruik</li>
+<li>gedefinieerde Intents en inhoudsprovider voor andere apps</li>
+<li>bugfixes</li>
+</ul>
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-nl/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-nl/help_nfc_beam.html
new file mode 100644
index 000000000..ac5cebb4b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nl/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>Hoe sleutels te ontvangen</h2>
+<ol>
+<li>Ga naar uw partners contacten en open het contact dat u wilt delen.</li>
+<li>Houd de twee apparaten met de achterkant tegen elkaar (ze moeten elkaar bijna aanraken) en u zult een trilling voelen.</li>
+<li>Nadat het trilt zult u de inhoud op uw partners apparaat in een soort kaart zien veranderen met Star Trek warp snelheid-eruitziende animatie op de achtergrond.</li>
+<li>Druk op de kaart en de inhoud zal dan laden op het apparaat.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-nl/help_start.html b/OpenKeychain/src/main/res/raw-nl/help_start.html
new file mode 100644
index 000000000..40c79e95f
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nl/help_start.html
@@ -0,0 +1,22 @@
+<html>
+<head></head>
+<body>
+<h2>Aan de slag</h2>
+<p>Eerst heeft u een persoonlijke geheime sleutel nodig. Maak er een via het opties menu in &quot;Sleutels&quot; of importeer bestaande geheime sleutels. Daarna kunt u de sleutels van uw vrienden importeren of ze uitwisselen via QR Codes of NFC.</p>
+
+<p>Het is aanbevolen dat u <a href="market://details?id=org.openintents.filemanager">OI Bestandsmanager</a> installeert voor betere bestandsselectie en <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> om gegenereerde QR codes te scannen. Door op de links te klikken zullen de Google Play Store of F-Droid openen voor installatie.</p>
+
+<h2>Applicaties</h2>
+<p>Verschillende applicaties ondersteunen OpenKeychain om uw privécommunicatie te coderen/signeren<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain ondersteuning beschikbaar in huidige <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversaties</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
+
+<h2>Ik heb een bug in OpenKeychain gevonden!</h2>
+<p>Rapporteer de bug met de <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">problemen tracker van OpenKeychain</a>.</p>
+
+<h2>Bijdragen</h2>
+<p>Als u ons wilt helpen om OpenKeychain te ontwikkelen door code bij te dragen, <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">volg onze kleine gids op Github</a>.</p>
+
+<h2>Vertalingen</h2>
+<p>Help OpenKeychain te vertalen! Iedereen kan deelnemen op <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain op Transifex</a>.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-nl/help_wot.html b/OpenKeychain/src/main/res/raw-nl/help_wot.html
new file mode 100644
index 000000000..efaac3c1e
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nl/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web van Vertrouwen</h2>
+<p>het Web of Trust beschrijft het deel van PGP dat te maken heeft met aanmaken en boekhouden van certificaten. Het voorziet van mechanismes zodat de gebruiker bij kan houden van aan wie een publieke sleutel toebehoort, en deze informatie met anderen kan delen; om de privacy van versleutelde communicatie te verzekeren, is het essentiëel om te weten dat de publieke sleutel die u versleutelt, toebehoort aan de persoon aan wie u denkt dat het toebehoort.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>Er is alleen basissupport voor Web van Vertrouwen in OpenKeychain. Dit is een zware 'werk in uitvoering' en onderwerp voor veranderingen in toekomstige releases.</p>
+
+<h2>Vertrouwen Model</h2>
+<p>Vertrouwen evaluatie is gebaseerd om de simpele aanname dat alle sleutels die beschikbare geheime sleutels hebben, vertrouwd zijn. Publieke sleutels die minstens een gebruikers-id bevatten, gecertificeerd door een vertrouwde sleutel, zullen gemarkeerd worden met een groene stip in de sleutel lijsten. Het is (nog) niet mogelijk om vertrouwen niveaus voor certificaten van andere bekende publieke sleutels te specifiëren.</p>
+
+<h2>Sleutels certificeren</h2>
+<p>Support voor sleutelcertificatie is beschikbaar, en gebruikers-id's kunnen individueel gecertificeerd worden. Het is nog niet mogelijk om het niveau van vertrouwen te specifiëren om locale en andere speciale typen van certificaten te maken.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-nl/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-nl/nfc_beam_share.html
new file mode 100644
index 000000000..5f7a63050
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-nl/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>Zorg ervoor dat NFC ingeschakeld is in Instellingen &gt; Meer &gt; NFC en zorg ervoor dat Android Beam ook in dezelfde sectie ingeschakeld is.</li>
+<li>Houd de twee apparaten met de achterkant tegen elkaar (ze moeten elkaar bijna aanraken) en u zult een trilling voelen.</li>
+<li>Nadat het trilt zult u de inhoud op uw partners apparaat in een soort kaart zien veranderen met Star Trek warp snelheid-eruitziende animatie op de achtergrond.</li>
+<li>Druk op de kaart en de inhoud zal dan laden op het apparaat van de andere persoon.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-pl/help_about.html b/OpenKeychain/src/main/res/raw-pl/help_about.html
index a033c084a..68cc2810e 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_about.html
+++ b/OpenKeychain/src/main/res/raw-pl/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Deweloperzy APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-pl/help_changelog.html b/OpenKeychain/src/main/res/raw-pl/help_changelog.html
index 8fce6c475..d94ac3c0f 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-pl/help_changelog.html
@@ -136,7 +136,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
<ul>
<li>dodano możliwość filtrowania listy kluczy</li>
<li>sprytniejsze automatyczne wybieranie kluczy szyfrujących</li>
-<li>dodano nowy sposób obsługi intencji "wyświetl" i "wyślij", umożliwia szyfrowanie/deszyfrowanie plików wprost z menadżera plików.</li>
+<li>dodano nowy sposób obsługi intencji &quot;wyświetl&quot; i &quot;wyślij&quot;, umożliwia szyfrowanie/deszyfrowanie plików wprost z menadżera plików.</li>
<li>poprawki i dodatkowe funkcje (podpowiedź wyboru klucza) dla K-9 Mail, nowe wydanie beta dostępne</li>
</ul>
<h2>1.0.1</h2>
diff --git a/OpenKeychain/src/main/res/raw-pl/help_start.html b/OpenKeychain/src/main/res/raw-pl/help_start.html
index ca10ec4b1..36ff31548 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_start.html
+++ b/OpenKeychain/src/main/res/raw-pl/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Pierwsze kroki</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>Zalecana jest instalacja menadżera plików <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> w celu zapewnienia wygodniejszego wyboru plików oraz programu <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a>, który jest w stanie skanować wygenerowane kody QR. Kliknięcie na powyższe linki przekieruje Cię do sklepu Google Play / F-Droid.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>Znalazłem błąd w OpenKeychain!</h2>
<p>Zgłoś błąd korzystając z <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">systemu śledzenia błędów OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-pl/help_wot.html b/OpenKeychain/src/main/res/raw-pl/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-pl/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-ru/help_about.html b/OpenKeychain/src/main/res/raw-ru/help_about.html
index 29cc1af83..4ffd2ba18 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_about.html
+++ b/OpenKeychain/src/main/res/raw-ru/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Разработчики APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-ru/help_changelog.html b/OpenKeychain/src/main/res/raw-ru/help_changelog.html
index cbd689629..fc258a623 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-ru/help_changelog.html
@@ -3,11 +3,11 @@
<body>
<h2>2.7</h2>
<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Пурпурный! (Dominik, Vincent)</li>
+<li>Новый вид просмотра ключей (Dominik, Vincent)</li>
+<li>Новый вид кнопок в стиле Android (Dominik, Vincent)</li>
+<li>Исправления API (Dominik)</li>
+<li>Импорт Keybase.io (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-ru/help_start.html b/OpenKeychain/src/main/res/raw-ru/help_start.html
index 022b80afc..9534acead 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_start.html
+++ b/OpenKeychain/src/main/res/raw-ru/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Приступая</h2>
-<p>Для начала Вас нужен свой секретный ключ. Создайте его в меню раздела "Ключи" или импортируйте ранее созданный ключ. Позже вы сможете поделиться данными своего ключа с друзьями с помощью QR кода или NFC.</p>
+<p>Для начала Вас нужен свой секретный ключ. Создайте его в меню раздела &quot;Ключи&quot; или импортируйте ранее созданный ключ. Позже вы сможете поделиться данными своего ключа с друзьями с помощью QR кода или NFC.</p>
<p>Рекомендуется установить <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> для удобного выбора файлов и <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> для распознавания QR кодов. Перейдите по ссылкам на соответствующие страницы Google Play или F-Droid для дальнейшей установки.</p>
-<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<h2>Приложения</h2>
+<p>Некоторые приложения, поддерживающие интеграцию с OpenKeychain для шифрования:<br><img src="apps_k9"><br>K-9 Mail: поддержка OpenKeychain доступна в текущей <a href="https://github.com/k9mail/k-9/releases/tag/4.904">альфа-версии</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: клиент Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: Приложение PGP-подписанных запросов на сервер для открытия или закрытия чего-либо.</p>
<h2>Я нашел ошибку в OpenKeychain!</h2>
<p>Пожалуйста, сообщайте о возникших проблемах и найденных ошибках в разделе <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">Решение проблем OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-ru/help_wot.html b/OpenKeychain/src/main/res/raw-ru/help_wot.html
new file mode 100644
index 000000000..53c862cbc
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-ru/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Сеть доверия (WoT)</h2>
+<p>Сеть доверия (The Web of Trust) предоставляет механизм, определения принадлежности ключа именно тому пользователю, который в нём указан. Основа этой модели строится на знакомстве людей и сертификации своей подписью ключей своих знакомых. Не менее важно распространение этой информации среди других пользователей WoT.</p>
+
+<h2>Поддержка в OpenKeychain</h2>
+<p>В настоящее время реализованы только базовые возможности WoT. Работа в самом разгаре и в будущих версиях OpenKeychain будут появляться дополнительные возможности.</p>
+
+<h2>Модель доверия</h2>
+<p>Проверка доверия основана на простом принципе, что если доступен секретный ключ, то доверие к нему абсолютное. Однако, публичные должны содержать подписи кого-то, кому вы доверяете, что бы подпись, сделанная таким ключом, была отмечена зелёным индикатором. К сожалению, (пока) нельзя определять степень доверия к другим известным публичным ключам.</p>
+
+<h2>Сертификация ключей</h2>
+<p>Поддержка сертификации ключей уже реализована, и Вы можете сертифицировать отдельные ID пользователя. Пока нельзя выбирать степень доверия, создавать локальные или специальные типы сертификатов.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-sl/help_about.html b/OpenKeychain/src/main/res/raw-sl/help_about.html
index a57b0e26f..40c487265 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_about.html
+++ b/OpenKeychain/src/main/res/raw-sl/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Razvijalci APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-sl/help_changelog.html b/OpenKeychain/src/main/res/raw-sl/help_changelog.html
index bc282fc66..1b48552ab 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-sl/help_changelog.html
@@ -3,11 +3,11 @@
<body>
<h2>2.7</h2>
<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Vijolična! (Dominik, Vincent)</li>
+<li>Nova podoba za ključe (Dominik, Vincent)</li>
+<li>Nova podoba - ploski androiidni gumbi (Dominik, Vincent)</li>
+<li>Popravki API (Dominik)</li>
+<li>Uboz iz Keybase.io (Tim Bray)</li>
</ul>
<h2>2.6.1</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-sl/help_start.html b/OpenKeychain/src/main/res/raw-sl/help_start.html
index 2b78e289a..4b02415d5 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_start.html
+++ b/OpenKeychain/src/main/res/raw-sl/help_start.html
@@ -6,10 +6,8 @@
<p>Priporočamo, da namestite aplikaciji <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> za boljše delo z datotekami in <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> za skeniranje kod QR. Klik na povezavi bo odprl trgovino Google Play Store ali F-Droid za namestitev.</p>
-<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<h2>Aplikacije</h2>
+<p>Razne aplikacije podpirajo OpenKeychain za šifriranje/podpisovanje vaših zasebnih komunikacij:<br><img src="apps_k9"><br>K-9 Mail: Podpora za OpenKeychain je na voljo v trenutni <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alfa različici</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Odjemalec za Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: Aplikacija za pošiljanje PGP-podpisanih zahtevkov strežnikom za odpiranje ali zapiranje česa, npr vrat</p>
<h2>Našel sem 'hrošča' v aplikaciji OpenKeychain!</h2>
<p>Za poročanje o 'hroščih' uporabite <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">sledilnik težav za OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-sl/help_wot.html b/OpenKeychain/src/main/res/raw-sl/help_wot.html
new file mode 100644
index 000000000..62cdf8094
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-sl/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Omrežje zaupanja</h2>
+<p>Omrežje zaupanja je del PGP-ja, ki se dotika ustvarjanja, urejanja in hranjenja ključev. Ponuja nam orodja za vodenje evidence ključev in njihovih lastnikov, ter deljenje teh informacij z drugimi. Za zagotavljanje zasebnosti šifrirane komunikacije je bistvenega pomena, da vemo, da ključ s katerim šifriramo določeno vsebino resnično pripada osebi, ki ji je ta vsebina namenjena.</p>
+
+<h2>Podpora v OpenKeychain</h2>
+<p>OpenKeychain ponuja zgolj osnovno podporo za 'Omrežje zaupanja'. V prihodnjih različicah bomo to podporo še nadgrajevali in izboljševali.</p>
+
+<h2>Model zaupanja</h2>
+<p>Ocena zaupanja temelji na predpostavki, da so vsi ključi, ki vsebujejo tudi zasebne ključe, vredni zaupanja. Javni ključi, ki vsebujejo vsaj en uporabniški ID, ki je overjen z zaupanja vrednim ključem, so na seznamu ključev označeni z zeleno piko. Zaenkrat (še) ni možno natančneje določiti stopnje zaupanja potrdil drugih znanih javnih ključev.</p>
+
+<h2>Overjanje ključev</h2>
+<p>Overjanje ključev je na voljo. Uporabniške ID-je ključev overjamo individualno. Zaenkrat ni možno natančneje določiti stopnje zaupanja ali ustvarjati lokalne ali druge posebne vrste potrdil.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-tr/help_about.html b/OpenKeychain/src/main/res/raw-tr/help_about.html
index 7d2c24f9c..db577c6a3 100644
--- a/OpenKeychain/src/main/res/raw-tr/help_about.html
+++ b/OpenKeychain/src/main/res/raw-tr/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Geliştiriciler APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-tr/help_start.html b/OpenKeychain/src/main/res/raw-tr/help_start.html
index 6ded872ea..3a681f8fa 100644
--- a/OpenKeychain/src/main/res/raw-tr/help_start.html
+++ b/OpenKeychain/src/main/res/raw-tr/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>I found a bug in OpenKeychain!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-tr/help_wot.html b/OpenKeychain/src/main/res/raw-tr/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-tr/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-uk/help_about.html b/OpenKeychain/src/main/res/raw-uk/help_about.html
index b51b80617..f6e65071c 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_about.html
+++ b/OpenKeychain/src/main/res/raw-uk/help_about.html
@@ -19,6 +19,7 @@
<li>Пауль Сарбіновський</li>
<li>Срірам Вояпаті</li>
<li>Вінсент Брейтмозер</li>
+<li>Тім Брей</li>
</ul>
<h2>Розробники APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-uk/help_changelog.html b/OpenKeychain/src/main/res/raw-uk/help_changelog.html
index e47c6bf98..b40eca76e 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_changelog.html
+++ b/OpenKeychain/src/main/res/raw-uk/help_changelog.html
@@ -3,20 +3,20 @@
<body>
<h2>2.7</h2>
<ul>
-<li>Purple! (Dominik, Vincent)</li>
-<li>New key view design (Dominik, Vincent)</li>
-<li>New flat Android buttons (Dominik, Vincent)</li>
-<li>API fixes (Dominik)</li>
-<li>Keybase.io import (Tim Bray)</li>
+<li>Багряний! (Домінік, Вінсент)</li>
+<li>Новий дизайн огляду ключа (Домінік, Вінсент)</li>
+<li>Нові плоскі кнопки Андроїд(Домінік, Вінсент)</li>
+<li>виправлення API (Домінік)</li>
+<li>Імпорт Keybase.io (Тім Брей)</li>
</ul>
<h2>2.6.1</h2>
<ul>
-<li>some fixes for regression bugs</li>
+<li>деякі виправлення для накопичених вад</li>
</ul>
<h2>2.6</h2>
<ul>
<li>сертифікації ключів (завдяки Вінсенту Бреймозеру)</li>
-<li>support for GnuPG partial secret keys (thanks to Vincent Breitmoser)</li>
+<li>підтримка часткових секретних ключів для GnuPG (завдяки Вінсенту Брейтмозеру)</li>
<li>новий дизайн для перевірки підпису</li>
<li>власна довжина ключа (завдяки Ґреґу Вітчаку)</li>
<li>виправлено функцію поширення з інших програм</li>
diff --git a/OpenKeychain/src/main/res/raw-uk/help_start.html b/OpenKeychain/src/main/res/raw-uk/help_start.html
index 1a4a7fab0..5caf05cc0 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_start.html
+++ b/OpenKeychain/src/main/res/raw-uk/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>Приступаючи до роботи</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>Спершу вам потрібний особистий секретний ключ. Створіть один через меню параметрів у „Ключі&quot; або імпортуйте наявні секретні ключі через &quot;Імпорт ключів&quot;. Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.</p>
<p>Рекомендуємо вам встановити <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> для поліпшеного виділення файлів та <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> для сканування згенерованих штрих-кодів. Натискання посилань відкриє Google Play або F-Droid для встановлення.</p>
-<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<h2>Програми</h2>
+<p>Окремі програми підтримують OpenKeychain для шифрування/підписування вашо спілкування:<br><img src="apps_k9"><br>K-9 Mail: підтримка OpenKeychain доступна у поточній <a href="https://github.com/k9mail/k-9/releases/tag/4.904">альфа-збірці</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Спілкування</a>: клієнт Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: програма надіслала запит, підписаний PGP, на сервер для відкриття чи закриття чогось на кшталт дверей</p>
<h2>Я знайшов помилку в OpenPGP Keychain!</h2>
<p>Будь ласка, повідомте про ваду за допомогою <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">відстежувача проблем OpenPGP Keychain</a>.</p>
diff --git a/OpenKeychain/src/main/res/raw-uk/help_wot.html b/OpenKeychain/src/main/res/raw-uk/help_wot.html
new file mode 100644
index 000000000..e1296472e
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-uk/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Мережа довіри</h2>
+<p>Мережа довіри - The Web of Trust - надає механізм визначення приналежності ключа саме тому користувачеві, який в ньому зазначений. Основа цієї моделі будується на знайомстві людей та сертифікації своїм підписом ключів своїх знайомих. Не менш важливо поширення цієї інформації серед інших користувачів WoT.</p>
+
+<h2>Підтримка у OpenKeychain</h2>
+<p>Наразі реалізовані тільки базові можливості WoT. Робота в самому розпалі, і в майбутніх версіях OpenKeychain будуть з'являтися додаткові можливості.</p>
+
+<h2>Модель довіри</h2>
+<p>Перевірка довіри заснована на простому принципі, що якщо доступний секретний ключ, то довіра до нього абсолютна. Однак, публічні повинні містити підписи когось, кому ви довіряєте, що б підпис, зроблений таким ключем, був відзначений зеленим індикатором. На жаль, (поки що) не можна визначати ступінь довіри до інших відомих публічних ключів.</p>
+
+<h2>Сертифікація ключів</h2>
+<p>Підтримка сертифікації ключів вже реалізована, і Ви можете сертифікувати окремі ID користувача. Наразі не можна вибирати ступінь довіри, створювати локальні або спеціальні типи сертифікатів.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-zh/help_about.html b/OpenKeychain/src/main/res/raw-zh/help_about.html
index 813676ea2..c9f76dd7c 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_about.html
+++ b/OpenKeychain/src/main/res/raw-zh/help_about.html
@@ -19,6 +19,7 @@
<li>Paul Sarbinowski</li>
<li>Sreeram Boyapati</li>
<li>Vincent Breitmoser</li>
+<li>Tim Bray</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
diff --git a/OpenKeychain/src/main/res/raw-zh/help_start.html b/OpenKeychain/src/main/res/raw-zh/help_start.html
index 7abb144a0..705bbcceb 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_start.html
+++ b/OpenKeychain/src/main/res/raw-zh/help_start.html
@@ -2,14 +2,12 @@
<head></head>
<body>
<h2>快速上手</h2>
-<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal secret key. Create one via the option menus in &quot;Keys&quot; or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Applications</h2>
-<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
-<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
-<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
+<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
<h2>我在OpenKeychain發現問題!</h2>
<p>請利用 <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">OpenKeychain 項目回報系統</a>回報問題。</p>
diff --git a/OpenKeychain/src/main/res/raw-zh/help_wot.html b/OpenKeychain/src/main/res/raw-zh/help_wot.html
new file mode 100644
index 000000000..29790139b
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-zh/help_wot.html
@@ -0,0 +1,17 @@
+<html>
+<head></head>
+<body>
+<h2>Web of Trust</h2>
+<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
+
+<h2>Support in OpenKeychain</h2>
+<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
+
+<h2>Trust Model</h2>
+<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
+
+<h2>Certifying keys</h2>
+<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
+
+</body>
+</html>
diff --git a/OpenKeychain/src/main/res/raw-zh/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-zh/nfc_beam_share.html
index 99ffe4c12..78f00c9f7 100644
--- a/OpenKeychain/src/main/res/raw-zh/nfc_beam_share.html
+++ b/OpenKeychain/src/main/res/raw-zh/nfc_beam_share.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<ol>
-<li>確定在 "設定" &gt; "更多內容…" &gt; "NFC" 裡面已經開啟 NFC 和 Android Beam。</li>
+<li>確定在 &quot;設定&quot; &gt; &quot;更多內容…&quot; &gt; &quot;NFC&quot; 裡面已經開啟 NFC 和 Android Beam。</li>
<li>將兩部裝置背對背貼近(幾乎接觸),你會感覺到一股震動。</li>
<li>震動之後你會看見你夥伴的畫面變成卡片狀,並且背景帶有如 Star Trek 般的特效。</li>
<li>輕觸卡片,內容隨即顯示在你的裝置上。</li>
diff --git a/OpenKeychain/src/main/res/values-fa-rIR/strings.xml b/OpenKeychain/src/main/res/values-ar/strings.xml
index e3d3a6493..e3d3a6493 100644
--- a/OpenKeychain/src/main/res/values-fa-rIR/strings.xml
+++ b/OpenKeychain/src/main/res/values-ar/strings.xml
diff --git a/OpenKeychain/src/main/res/values-cs-rCZ/strings.xml b/OpenKeychain/src/main/res/values-cs-rCZ/strings.xml
deleted file mode 100644
index c3d7cc4c9..000000000
--- a/OpenKeychain/src/main/res/values-cs-rCZ/strings.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
- <!--title-->
- <string name="title_select_recipients">Zvolit veřejný klíč</string>
- <string name="title_select_secret_key">Zvolit tajný klíč</string>
- <string name="title_encrypt">Zašifrovat</string>
- <string name="title_decrypt">Dešifrovat</string>
- <string name="title_authentication">Heslo</string>
- <string name="title_create_key">Vytvořit klíč</string>
- <string name="title_edit_key">Upravit klíč</string>
- <string name="title_preferences">Nastavení</string>
- <string name="title_api_registered_apps">Registrované aplikace</string>
- <string name="title_key_server_preference">Nastavení serveru s klíči</string>
- <string name="title_set_passphrase">Zadat heslo</string>
- <string name="title_import_keys">Importovat klíče</string>
- <string name="title_export_key">Exportovat klíč</string>
- <string name="title_export_keys">Exportovat klíče</string>
- <string name="title_key_not_found">Klíč nenalezen</string>
- <string name="title_send_key">Nahrát na server s klíči</string>
- <string name="title_help">Nápověda</string>
- <!--section-->
- <!--button-->
- <!--menu-->
- <!--label-->
- <!--choice-->
- <!--key flags-->
- <!--sentences-->
- <!--errors
- no punctuation, all lowercase,
- they will be put after "error_message", e.g. "Error: file not found"-->
- <!--errors without preceeding Error:-->
- <!--results shown after decryption/verification-->
- <!--progress dialogs, usually ending in '…'-->
- <!--action strings-->
- <!--key bit length selections-->
- <!--compression-->
- <!--Help-->
- <!--Import-->
- <!--Intent labels-->
- <!--Remote API-->
- <!--Share-->
- <!--Key list-->
- <!--Key view-->
- <!--Navigation Drawer-->
- <!--hints-->
- <!--certs-->
- <!--unsorted-->
-</resources>
diff --git a/OpenKeychain/src/main/res/values-cs/strings.xml b/OpenKeychain/src/main/res/values-cs/strings.xml
new file mode 100644
index 000000000..e3d3a6493
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-cs/strings.xml
@@ -0,0 +1,31 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <!--section-->
+ <!--button-->
+ <!--menu-->
+ <!--label-->
+ <!--choice-->
+ <!--key flags-->
+ <!--sentences-->
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <!--errors without preceeding Error:-->
+ <!--results shown after decryption/verification-->
+ <!--progress dialogs, usually ending in '…'-->
+ <!--action strings-->
+ <!--key bit length selections-->
+ <!--compression-->
+ <!--Help-->
+ <!--Import-->
+ <!--Intent labels-->
+ <!--Remote API-->
+ <!--Share-->
+ <!--Key list-->
+ <!--Key view-->
+ <!--Navigation Drawer-->
+ <!--hints-->
+ <!--certs-->
+ <!--unsorted-->
+</resources>
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index 61d4fc642..e2dfa196b 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -273,9 +273,6 @@
<string name="error_jelly_bean_needed">Android 4.1 wird benötigt um Androids NFC Beam nutzen zu können!</string>
<string name="error_nfc_needed">NFC steht auf diesem Gerät nicht zur Verfügung!</string>
<string name="error_nothing_import">Nichts zu importieren!</string>
- <string name="error_keyserver_insufficient_query">Unzureichende Serveranfrage</string>
- <string name="error_keyserver_query">Anfrage fehlgeschlagen</string>
- <string name="error_keyserver_too_many_responses">Zu viele mögliche Schlüssel. Verfeinere deine Suchanfrage!</string>
<string name="error_import_file_no_content">Datei ist leer</string>
<string name="error_generic_report_bug">Ein allgemeiner Fehler trat auf, bitte schreiben Sie einen neuen Bugreport für OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
@@ -330,6 +327,7 @@
<string name="hint_public_keys">Öffentliche Schlüssel suchen</string>
<string name="hint_secret_keys">Private Schlüssel suchen</string>
<string name="action_share_key_with">Teile Schlüssel über…</string>
+ <string name="hint_keybase_search">Durchsuche Keybase.io</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_768">768</string>
@@ -369,12 +367,14 @@
<string name="import_nfc_text">Um Schlüssel über NFC zu erhalten muss das Gerät entsperrt sein.</string>
<string name="import_nfc_help_button">Hilfe</string>
<string name="import_clipboard_button">Schlüssel aus der Zwischenablage einfügen</string>
+ <string name="import_keybase_button">Schlüssel von Keybase.io erhalten</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Datei entschlüsseln mit OpenKeychain</string>
<string name="intent_import_key">Schlüssel importieren mit OpenKeychain</string>
<string name="intent_send_encrypt">Verschlüsseln mit OpenKeychain </string>
<string name="intent_send_decrypt">Entschlüsseln mit OpenKeychain </string>
<!--Remote API-->
+ <string name="api_no_apps">Keine verknüpften Apps vorhanden\n\nEine Liste von unterstützten Drittanbieter-Apps finden Sie unter \"Hilfe\"!</string>
<string name="api_settings_show_info">Erweiterte Informationen anzeigen</string>
<string name="api_settings_hide_info">Erweiterte Informationen ausblenden</string>
<string name="api_settings_show_advanced">Erweiterte Einstellungen anzeigen</string>
@@ -385,6 +385,7 @@
<string name="api_settings_save">Speichern</string>
<string name="api_settings_cancel">Abbrechen</string>
<string name="api_settings_revoke">Zugriff widerufen</string>
+ <string name="api_settings_start">Starte Anwendung</string>
<string name="api_settings_delete_account">Account löschen</string>
<string name="api_settings_package_name">Paketname</string>
<string name="api_settings_package_signature">SHA-256 der Paketsignatur</string>
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index dd628f87a..45d3d565b 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -273,9 +273,9 @@
<string name="error_jelly_bean_needed">¡Necesita Android 4.1 para usar la característica NFC Beam (haz NFC) de Android!</string>
<string name="error_nfc_needed">¡NFC no está disponible en tu dispositivo!</string>
<string name="error_nothing_import">¡Nada que importar!</string>
- <string name="error_keyserver_insufficient_query">Consulta al servidor insuficiente</string>
- <string name="error_keyserver_query">La consulta al servidor de claves ha fallado</string>
- <string name="error_keyserver_too_many_responses">Demasiadas claves posibles. Por favor ¡refine su consulta!</string>
+ <string name="error_keyserver_insufficient_query">Petición de búsqueda de clave demasiado corta</string>
+ <string name="error_searching_keys">Error irrecuperable buscando claves en el servidor</string>
+ <string name="error_keyserver_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string>
<string name="error_import_file_no_content">El archivo está vacio</string>
<string name="error_generic_report_bug">Ha ocurrido un error genérico, por favor, informa de este bug a OpenKeychain</string>
<plurals name="error_import_non_pgp_part">
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index 575ce8085..f49127b6f 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -273,9 +273,9 @@
<string name="error_jelly_bean_needed">Il vous faut Android 4.1 pour utiliser la fonction Beam NFC d\'Android !</string>
<string name="error_nfc_needed">La NFC n\'est pas disponible sur votre appareil !</string>
<string name="error_nothing_import">Rien à importer !</string>
- <string name="error_keyserver_insufficient_query">Requête serveur insuffisante</string>
- <string name="error_keyserver_query">Échec lors de l\'interrogation du serveur de clefs</string>
- <string name="error_keyserver_too_many_responses">Il y a trop de clefs possibles. Veuillez affiner votre requête !</string>
+ <string name="error_keyserver_insufficient_query">La requête de recherche de clef est trop courte</string>
+ <string name="error_searching_keys">Erreur irrécupérable lors de la recherche de clef sur le serveur</string>
+ <string name="error_keyserver_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string>
<string name="error_import_file_no_content">Le fichier n\'a pas de contenu</string>
<string name="error_generic_report_bug">Une erreur générique est survenue, veuillez créer un nouveau rapport de bogue pour OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
diff --git a/OpenKeychain/src/main/res/values-it-rIT/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml
index b951bcf71..eae4dd4af 100644
--- a/OpenKeychain/src/main/res/values-it-rIT/strings.xml
+++ b/OpenKeychain/src/main/res/values-it/strings.xml
@@ -273,9 +273,6 @@
<string name="error_jelly_bean_needed">Devi avere Android 4.1 per usare Android NFC Beam!</string>
<string name="error_nfc_needed">NFC non disponibile nel tuo dispositivo!</string>
<string name="error_nothing_import">Niente da importare!</string>
- <string name="error_keyserver_insufficient_query">Query di server insufficiente</string>
- <string name="error_keyserver_query">Interrogazione del server delle chiavi fallita</string>
- <string name="error_keyserver_too_many_responses">Troppe chiavi corrispondenti. Riformula meglio la ricerca!</string>
<string name="error_import_file_no_content">Il File non ha contenuti</string>
<string name="error_generic_report_bug">Si è verificato un errore generico, si prega di creare una nuova segnalazione di errore per OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
@@ -377,6 +374,7 @@
<string name="intent_send_encrypt">Codifica con OpenKeychain</string>
<string name="intent_send_decrypt">Decodifica con OpenKeychain</string>
<!--Remote API-->
+ <string name="api_no_apps">Nessuna applicazione registrata!\n\nUna lista di applicazioni di terze parti supportate è disponibile in \'Aiuto\'!</string>
<string name="api_settings_show_info">Mostra informazioni dettagliate</string>
<string name="api_settings_hide_info">Nascondi informazioni dettagliate</string>
<string name="api_settings_show_advanced">Mostra impostazioni avanzate</string>
@@ -387,6 +385,7 @@
<string name="api_settings_save">Salva</string>
<string name="api_settings_cancel">Annulla</string>
<string name="api_settings_revoke">Revoca accesso</string>
+ <string name="api_settings_start">Avvia applicazione</string>
<string name="api_settings_delete_account">Cancella account</string>
<string name="api_settings_package_name">Nome Pacchetto</string>
<string name="api_settings_package_signature">SHA-256 della Firma del Pacchetto</string>
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index 1bcf4553b..c40e9dbdc 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -264,9 +264,9 @@
<string name="error_jelly_bean_needed">Android NFC Beam機能を使うにはAndroid 4.1 が必要です!</string>
<string name="error_nfc_needed">あなたのデバイスにはNFCが存在しません!</string>
<string name="error_nothing_import">インポートするものがありません!</string>
- <string name="error_keyserver_insufficient_query">サーバへのクエリーが不足しています</string>
- <string name="error_keyserver_query">鍵サーバへのクエリーが失敗</string>
- <string name="error_keyserver_too_many_responses">鍵が多すぎます。クエリーを整えてください!</string>
+ <string name="error_keyserver_insufficient_query">鍵検索のクエリが短かすぎます</string>
+ <string name="error_searching_keys">サーバでの鍵の検索が回復不可能なエラーになりました</string>
+ <string name="error_keyserver_too_many_responses">鍵検索のクエリが沢山の候補を返しました; クエリを精密化してください</string>
<string name="error_import_file_no_content">ファイルに内容がありません</string>
<string name="error_generic_report_bug">一般エラーが発生しました、この新しいバグの情報をOpenKeychainプロジェクトに送ってください</string>
<plurals name="error_import_non_pgp_part">
diff --git a/OpenKeychain/src/main/res/values-nl-rNL/strings.xml b/OpenKeychain/src/main/res/values-nl-rNL/strings.xml
deleted file mode 100644
index bf6d7f911..000000000
--- a/OpenKeychain/src/main/res/values-nl-rNL/strings.xml
+++ /dev/null
@@ -1,198 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
- <!--title-->
- <string name="title_select_recipients">Publieke sleutel selecteren</string>
- <string name="title_select_secret_key">Privésleutel selecteren</string>
- <string name="title_encrypt">Versleutelen</string>
- <string name="title_decrypt">Ontsleutelen</string>
- <string name="title_authentication">Wachtwoord</string>
- <string name="title_create_key">Sleutel aanmaken</string>
- <string name="title_edit_key">Sleutel bewerken</string>
- <string name="title_preferences">Instellingen</string>
- <string name="title_api_registered_apps">Geregistreerde apps</string>
- <string name="title_set_passphrase">Wachtwoord instellen</string>
- <string name="title_encrypt_to_file">Versleutelen naar bestand</string>
- <string name="title_decrypt_to_file">Ontsleutelen naar bestand</string>
- <string name="title_import_keys">Sleutels importeren</string>
- <string name="title_export_key">Sleutels exporteren</string>
- <string name="title_export_keys">Sleutels exporteren</string>
- <string name="title_key_not_found">Sleutel niet gevonden</string>
- <string name="title_help">Help</string>
- <!--section-->
- <string name="section_general">Algemeen</string>
- <string name="section_defaults">Standaard</string>
- <string name="section_advanced">Geavanceerd</string>
- <!--button-->
- <string name="btn_save">Opslaan</string>
- <string name="btn_do_not_save">Annuleren</string>
- <string name="btn_delete">Verwijderen</string>
- <string name="btn_no_date">Geen</string>
- <string name="btn_okay">OK</string>
- <string name="btn_next">Volgende</string>
- <string name="btn_back">Terug</string>
- <!--menu-->
- <string name="menu_preferences">Instellingen</string>
- <string name="menu_import_from_file">Importeren uit bestand</string>
- <string name="menu_import_from_qr_code">Importeren met QR-code</string>
- <string name="menu_import_from_nfc">Importeren met NFC</string>
- <string name="menu_export_key">Exporteren naar bestand</string>
- <string name="menu_delete_key">Sleutel verwijderen</string>
- <string name="menu_create_key">Sleutel aanmaken</string>
- <string name="menu_create_key_expert">Sleutel aanmaken (expert)</string>
- <string name="menu_search">Zoeken</string>
- <string name="menu_beam_preferences">Beam-instellingen</string>
- <!--label-->
- <string name="label_sign">Ondertekenen</string>
- <string name="label_message">Bericht</string>
- <string name="label_file">Bestand</string>
- <string name="label_no_passphrase">Geen wachtwoord</string>
- <string name="label_passphrase">Wachtwoord</string>
- <string name="label_passphrase_again">Opnieuw</string>
- <string name="label_algorithm">Algoritme</string>
- <string name="label_ascii_armor">ASCII-armor</string>
- <string name="label_delete_after_encryption">Verwijderen na versleuteling</string>
- <string name="label_delete_after_decryption">Verwijderen na ontsleuteling</string>
- <string name="label_encryption_algorithm">Versleutelingsalgoritme</string>
- <string name="label_hash_algorithm">Verificatie-algoritme</string>
- <string name="label_passphrase_cache_ttl">Wachtwoordcache</string>
- <string name="label_message_compression">Berichtcompressie</string>
- <string name="label_file_compression">Bestandscompressie</string>
- <string name="label_key_id">Sleutel-id</string>
- <string name="label_creation">Aanmaak</string>
- <string name="label_expiry">Verlopen</string>
- <string name="label_usage">Gebruik</string>
- <string name="label_key_size">Sleutelgrootte</string>
- <string name="label_name">Naam</string>
- <string name="label_comment">Opmerking</string>
- <string name="label_email">E-mailadres</string>
- <string name="none">&lt;geen&gt;</string>
- <string name="no_key">&lt;geen sleutel&gt;</string>
- <string name="can_encrypt">versleutelbaar</string>
- <string name="can_sign">ondertekenbaar</string>
- <string name="expired">verlopen</string>
- <string name="secret_key">Privésleutel:</string>
- <!--choice-->
- <string name="choice_none">Geen</string>
- <string name="choice_15secs">15 sec.</string>
- <string name="choice_1min">1 min.</string>
- <string name="choice_3mins">3 min.</string>
- <string name="choice_5mins">5 min.</string>
- <string name="choice_10mins">10 min.</string>
- <string name="choice_20mins">20 min.</string>
- <string name="choice_40mins">40 min.</string>
- <string name="choice_1hour">1 uur</string>
- <string name="choice_2hours">2 uur</string>
- <string name="choice_4hours">4 uur</string>
- <string name="choice_8hours">8 uur</string>
- <string name="dsa">DSA</string>
- <string name="elgamal">ElGamal</string>
- <string name="rsa">RSA</string>
- <string name="filemanager_title_open">Openen...</string>
- <string name="warning">Waarschuwing</string>
- <string name="error">Fout</string>
- <string name="error_message">Fout: %s</string>
- <!--key flags-->
- <!--sentences-->
- <string name="wrong_passphrase">Wachtwoord verkeerd.</string>
- <string name="set_a_passphrase">Stel eerst een wachtwoord in.</string>
- <string name="no_filemanager_installed">Geen compatibele bestandsbeheerder geïnstalleerd.</string>
- <string name="passphrases_do_not_match">De wachtwoorden komen niet overeen.</string>
- <string name="passphrase_for_symmetric_encryption">Symmetrische versleuteling.</string>
- <string name="passphrase_for">Voer het wachtwoord in voor \'%s\'</string>
- <string name="file_delete_confirmation">Weer u zeker dat u het volgende wilt verwijderen:\n%s?</string>
- <string name="file_delete_successful">Succesvol verwijderd.</string>
- <string name="no_file_selected">Selecteer eerst een bestand.</string>
- <string name="enter_passphrase_twice">Voer het wachtwoord tweemaal in.</string>
- <string name="select_encryption_key">Selecteer ten minste één versleutelingssleutel.</string>
- <string name="select_encryption_or_signature_key">Selecter ten minste één versleutelings-/ondertekeningssleutel.</string>
- <string name="secret_key_deletion_confirmation">Weet u zeker dat u de privésleutel \'%s\' wilt verwijderen?\nDit kan niet ongedaan worden gemaakt.</string>
- <string name="no_keys_added_or_updated">Geen sleutels toegevoegd of bijgewerkt.</string>
- <string name="key_exported">1 sleutel succesvol geëxporteerd.</string>
- <string name="no_keys_exported">Geen sleutels geëxporteerd.</string>
- <string name="key_not_found">Kan de sleutel %08X niet vinden.</string>
- <string name="list_empty">Lijst is leeg</string>
- <!--errors
- no punctuation, all lowercase,
- they will be put after "error_message", e.g. "Error: file not found"-->
- <string name="error_file_delete_failed">verwijderen \'%s\' mislukt</string>
- <string name="error_file_not_found">bestand niet gevonden</string>
- <string name="error_no_secret_key_found">geen geschikte privésleutel gevonden</string>
- <string name="error_external_storage_not_ready">externe opslag niet gereed</string>
- <string name="error_key_size_minimum512bit">sleutelgrootte moet minstens 512-bits zijn</string>
- <string name="error_master_key_must_not_be_el_gamal">de hoofdsleutel kan geen ElGamal-sleutel zijn</string>
- <string name="error_unknown_algorithm_choice">onbekende algoritmekeuze</string>
- <string name="error_key_needs_master_key">ten minste een hoofdsleutel is vereist</string>
- <string name="error_no_signature_passphrase">geen wachtwoord opgegeven</string>
- <string name="error_no_signature_key">geen ondertekeningssleutel opgegeven</string>
- <string name="error_invalid_data">geen geldige versleutelingsgegevens</string>
- <string name="error_wrong_passphrase">wachtwoord verekerd</string>
- <string name="error_could_not_extract_private_key">kan privésleutel niet uitpakken</string>
- <!--errors without preceeding Error:-->
- <string name="error_nfc_needed">Uw apparaat biedt geen ondersteuning voor NFC</string>
- <string name="error_nothing_import">Niets te importeren</string>
- <!--results shown after decryption/verification-->
- <!--progress dialogs, usually ending in '…'-->
- <string name="progress_saving">opslaan...</string>
- <string name="progress_importing">importeren...</string>
- <string name="progress_exporting">exporteren...</string>
- <string name="progress_building_key">sleutel maken...</string>
- <string name="progress_certifying_master_key">hoofdsleutel certificeren...</string>
- <string name="progress_building_master_key">hoofdsleutelbos maken...</string>
- <string name="progress_adding_sub_keys">sub-sleutels toevoegen...</string>
- <string name="progress_extracting_signature_key">ondertekeningssleutel uitpakken...</string>
- <string name="progress_extracting_key">sleutel uitpakken...</string>
- <string name="progress_preparing_streams">streams voorbereiden...</string>
- <string name="progress_encrypting">gegevens versleutelen...</string>
- <string name="progress_decrypting">gegevens ontsleutelen...</string>
- <string name="progress_preparing_signature">handtekening voorbereiden...</string>
- <string name="progress_generating_signature">handtekening genereren...</string>
- <string name="progress_processing_signature">handtekening verwerken...</string>
- <string name="progress_verifying_signature">handtekening verifiëren...</string>
- <string name="progress_signing">ondertekenen...</string>
- <string name="progress_reading_data">gegevens lezen...</string>
- <string name="progress_finding_key">sleutel opzoeken...</string>
- <string name="progress_decompressing_data">gegevens decomprimeren...</string>
- <string name="progress_verifying_integrity">integriteit verifiëren...</string>
- <string name="progress_deleting_securely">\'%s\' veilig verwijderen...</string>
- <!--action strings-->
- <string name="hint_public_keys">Publieke sleutels zoeken</string>
- <string name="hint_secret_keys">Privésleutels zoeken</string>
- <string name="action_share_key_with">Sleutel delen met...</string>
- <!--key bit length selections-->
- <string name="key_size_512">512</string>
- <string name="key_size_1024">1024</string>
- <string name="key_size_2048">2048</string>
- <string name="key_size_4096">4096</string>
- <!--compression-->
- <string name="compression_fast">snel</string>
- <string name="compression_very_slow">zeer langzaam</string>
- <!--Help-->
- <string name="help_tab_start">Beginnen</string>
- <string name="help_tab_nfc_beam">NFC Beam</string>
- <string name="help_tab_changelog">Lijst van wijzigingen</string>
- <string name="help_tab_about">Over</string>
- <string name="help_about_version">Versie:</string>
- <!--Import-->
- <string name="import_import">Geselecteerde sleutels importeren</string>
- <string name="import_qr_code_wrong">QR-code ongeldig. Probeer het opnieuw</string>
- <string name="import_qr_code_finished">QR-code gescand</string>
- <!--Intent labels-->
- <!--Remote API-->
- <string name="api_settings_no_key">Geen sleutel geselecteerd</string>
- <string name="api_settings_select_key">Sleutel selecteren</string>
- <string name="api_settings_save">Opslaan</string>
- <string name="api_settings_cancel">Annuleren</string>
- <string name="api_settings_revoke">Toegang herroepen</string>
- <string name="api_register_allow">Toegang toestaan</string>
- <string name="api_register_disallow">Toegang weigeren</string>
- <string name="api_register_error_select_key">Selecteert u a.u.b. een sleutel</string>
- <string name="api_select_pub_keys_text">Bekijkt u a.u.b. de ontvangers</string>
- <!--Share-->
- <string name="share_qr_code_dialog_start">U gaat door alle QR-codes met \'Volgende\', en scant ze een voor een.</string>
- <!--Key list-->
- <!--Key view-->
- <!--Navigation Drawer-->
- <!--hints-->
- <!--certs-->
- <!--unsorted-->
-</resources>
diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml
new file mode 100644
index 000000000..d35d83517
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-nl/strings.xml
@@ -0,0 +1,474 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <string name="title_select_recipients">Publieke sleutel selecteren</string>
+ <string name="title_select_secret_key">Privésleutel selecteren</string>
+ <string name="title_encrypt">Versleutelen</string>
+ <string name="title_decrypt">Ontsleutelen</string>
+ <string name="title_authentication">Wachtwoord</string>
+ <string name="title_create_key">Sleutel aanmaken</string>
+ <string name="title_edit_key">Sleutel bewerken</string>
+ <string name="title_preferences">Instellingen</string>
+ <string name="title_api_registered_apps">Geregistreerde apps</string>
+ <string name="title_key_server_preference">Sleutelserver Voorkeur</string>
+ <string name="title_change_passphrase">Wachtwoord wijzigen</string>
+ <string name="title_set_passphrase">Wachtwoord instellen</string>
+ <string name="title_share_with">Delen met...</string>
+ <string name="title_share_fingerprint_with">Vingerafdruk delen met...</string>
+ <string name="title_share_key">Sleutel delen met...</string>
+ <string name="title_share_file">Bestand delen met...</string>
+ <string name="title_encrypt_to_file">Versleutelen naar bestand</string>
+ <string name="title_decrypt_to_file">Ontsleutelen naar bestand</string>
+ <string name="title_import_keys">Sleutels importeren</string>
+ <string name="title_export_key">Sleutels exporteren</string>
+ <string name="title_export_keys">Sleutels exporteren</string>
+ <string name="title_key_not_found">Sleutel niet gevonden</string>
+ <string name="title_send_key">Upload naar Sleutelserver</string>
+ <string name="title_certify_key">Certifiëer Identiteiten</string>
+ <string name="title_key_details">Sleutel Details</string>
+ <string name="title_help">Help</string>
+ <!--section-->
+ <string name="section_user_ids">Identiteiten</string>
+ <string name="section_keys">Subsleutels</string>
+ <string name="section_general">Algemeen</string>
+ <string name="section_defaults">Standaard</string>
+ <string name="section_advanced">Geavanceerd</string>
+ <string name="section_master_key">Master Sleutel</string>
+ <string name="section_master_user_id">Primaire Identiteit</string>
+ <string name="section_actions">Acties</string>
+ <string name="section_share_key">Hele sleutel</string>
+ <string name="section_certification_key">Uw Sleutel die u gebruikt voor certificatie</string>
+ <string name="section_upload_key">Upload Sleutel</string>
+ <string name="section_key_server">Sleutelserver</string>
+ <string name="section_encrypt_and_or_sign">Versleutel en/of Signeer</string>
+ <string name="section_decrypt_verify">Decodeer en Verifiëer</string>
+ <string name="section_fingerprint">Vingerafdruk</string>
+ <string name="section_key_to_certify">Sleutel om te certificeren</string>
+ <!--button-->
+ <string name="btn_certify">Certifiëer</string>
+ <string name="btn_decrypt_verify_file">Decodeer, verifiëer en sla bestand op</string>
+ <string name="btn_decrypt_verify_message">Decodeer en verifiëer bericht</string>
+ <string name="btn_decrypt_verify_clipboard">Van Klembord</string>
+ <string name="btn_encrypt_file">Codeer en sla bestanden op</string>
+ <string name="btn_save">Opslaan</string>
+ <string name="btn_do_not_save">Annuleren</string>
+ <string name="btn_delete">Verwijderen</string>
+ <string name="btn_no_date">Geen</string>
+ <string name="btn_okay">OK</string>
+ <string name="btn_change_passphrase">Verander Nieuw Wachtwoord</string>
+ <string name="btn_set_passphrase">Bepaal Nieuwe Wachtwoord</string>
+ <string name="btn_export_to_server">Upload Naar Sleutelserver</string>
+ <string name="btn_next">Volgende</string>
+ <string name="btn_back">Terug</string>
+ <string name="btn_clipboard">Klembord</string>
+ <string name="btn_share">Delen met...</string>
+ <string name="btn_lookup_key">Opzoeksleutel</string>
+ <string name="btn_encryption_advanced_settings_show">Toon geavanceerde instellingen</string>
+ <string name="btn_encryption_advanced_settings_hide">Verberg geavanceerde instellingen</string>
+ <string name="btn_share_encrypted_signed">Gecodeerd/ondertekend bericht delen...</string>
+ <string name="btn_view_cert_key">Toon certificatiesleutel</string>
+ <!--menu-->
+ <string name="menu_preferences">Instellingen</string>
+ <string name="menu_help">Help</string>
+ <string name="menu_import_from_file">Importeren uit bestand</string>
+ <string name="menu_import_from_qr_code">Importeren met QR-code</string>
+ <string name="menu_import_from_nfc">Importeren met NFC</string>
+ <string name="menu_export_key">Exporteren naar bestand</string>
+ <string name="menu_delete_key">Sleutel verwijderen</string>
+ <string name="menu_create_key">Sleutel aanmaken</string>
+ <string name="menu_create_key_expert">Sleutel aanmaken (expert)</string>
+ <string name="menu_search">Zoeken</string>
+ <string name="menu_import_from_key_server">Sleutelserver</string>
+ <string name="menu_import_from_keybase">Importeren uit Keybase.io</string>
+ <string name="menu_key_server">Sleutelserver...</string>
+ <string name="menu_update_key">Update van sleutelserver</string>
+ <string name="menu_export_key_to_server">Upload naar sleutelserver</string>
+ <string name="menu_share">Delen...</string>
+ <string name="menu_share_title_fingerprint">Deel vingerafdruk...</string>
+ <string name="menu_share_title">Hele sleutel delen...</string>
+ <string name="menu_share_default_fingerprint">met...</string>
+ <string name="menu_share_default">met...</string>
+ <string name="menu_share_qr_code">met QR Code</string>
+ <string name="menu_share_qr_code_fingerprint">met QR Code</string>
+ <string name="menu_share_nfc">met NFC</string>
+ <string name="menu_copy_to_clipboard">Kopieer naar klembord</string>
+ <string name="menu_beam_preferences">Beam-instellingen</string>
+ <string name="menu_key_edit_cancel">Annuleren</string>
+ <string name="menu_encrypt_to">Versleutelen naar...</string>
+ <string name="menu_select_all">Alles selecteren</string>
+ <string name="menu_add_keys">Sleutels toevoegen</string>
+ <string name="menu_export_all_keys">Alle sleutels exporteren</string>
+ <!--label-->
+ <string name="label_sign">Ondertekenen</string>
+ <string name="label_message">Bericht</string>
+ <string name="label_file">Bestand</string>
+ <string name="label_no_passphrase">Geen wachtwoord</string>
+ <string name="label_passphrase">Wachtwoord</string>
+ <string name="label_passphrase_again">Opnieuw</string>
+ <string name="label_algorithm">Algoritme</string>
+ <string name="label_ascii_armor">ASCII-armor</string>
+ <string name="label_select_public_keys">Ontvangers</string>
+ <string name="label_delete_after_encryption">Verwijderen na versleuteling</string>
+ <string name="label_delete_after_decryption">Verwijderen na ontsleuteling</string>
+ <string name="label_share_after_encryption">Delen Na Versleuteling</string>
+ <string name="label_encryption_algorithm">Versleutelingsalgoritme</string>
+ <string name="label_hash_algorithm">Verificatie-algoritme</string>
+ <string name="label_asymmetric">met Publieke Sleutel</string>
+ <string name="label_symmetric">met Wachtwoord</string>
+ <string name="label_passphrase_cache_ttl">Wachtwoordcache</string>
+ <string name="label_message_compression">Berichtcompressie</string>
+ <string name="label_file_compression">Bestandscompressie</string>
+ <string name="label_force_v3_signature">Forceer oude OpenPGPv3 Handtekeningen</string>
+ <string name="label_keyservers">Sleutelservers</string>
+ <string name="label_key_id">Sleutel-id</string>
+ <string name="label_creation">Aanmaak</string>
+ <string name="label_expiry">Verlopen</string>
+ <string name="label_usage">Gebruik</string>
+ <string name="label_key_size">Sleutelgrootte</string>
+ <string name="label_main_user_id">Primaire identiteit</string>
+ <string name="label_name">Naam</string>
+ <string name="label_comment">Opmerking</string>
+ <string name="label_email">E-mailadres</string>
+ <string name="label_send_key">Upload sleutel naar geselecteerde sleutelserver na bevestiging</string>
+ <string name="label_fingerprint">Vingerafdruk</string>
+ <string name="select_keys_button_default">Selecteren</string>
+ <string name="expiry_date_dialog_title">Bepaal verloopdatum</string>
+ <plurals name="select_keys_button">
+ <item quantity="one">%d geselecteerd</item>
+ <item quantity="other">%d geselecteerd</item>
+ </plurals>
+ <string name="user_id_no_name">&lt;no naam&gt;</string>
+ <string name="none">&lt;geen&gt;</string>
+ <string name="no_key">&lt;geen sleutel&gt;</string>
+ <string name="can_encrypt">versleutelbaar</string>
+ <string name="can_sign">ondertekenbaar</string>
+ <string name="can_certify">kan certificeren</string>
+ <string name="can_certify_not">kan niet certificeren</string>
+ <string name="expired">verlopen</string>
+ <string name="revoked">ingetrokken</string>
+ <plurals name="n_keys">
+ <item quantity="one">1 sleutel</item>
+ <item quantity="other">%d sleutels</item>
+ </plurals>
+ <plurals name="n_keyservers">
+ <item quantity="one">%d sleutelserver</item>
+ <item quantity="other">%d sleutelservers</item>
+ </plurals>
+ <string name="secret_key">Privésleutel:</string>
+ <!--choice-->
+ <string name="choice_none">Geen</string>
+ <string name="choice_15secs">15 sec.</string>
+ <string name="choice_1min">1 min.</string>
+ <string name="choice_3mins">3 min.</string>
+ <string name="choice_5mins">5 min.</string>
+ <string name="choice_10mins">10 min.</string>
+ <string name="choice_20mins">20 min.</string>
+ <string name="choice_40mins">40 min.</string>
+ <string name="choice_1hour">1 uur</string>
+ <string name="choice_2hours">2 uur</string>
+ <string name="choice_4hours">4 uur</string>
+ <string name="choice_8hours">8 uur</string>
+ <string name="choice_forever">voor altijd</string>
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+ <string name="filemanager_title_open">Openen...</string>
+ <string name="warning">Waarschuwing</string>
+ <string name="error">Fout</string>
+ <string name="error_message">Fout: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Certificeer</string>
+ <string name="flag_sign">Ondertekenen</string>
+ <string name="flag_encrypt">Versleutelen</string>
+ <string name="flag_authenticate">Legitimeren</string>
+ <!--sentences-->
+ <string name="wrong_passphrase">Wachtwoord verkeerd.</string>
+ <string name="set_a_passphrase">Stel eerst een wachtwoord in.</string>
+ <string name="no_filemanager_installed">Geen compatibele bestandsbeheerder geïnstalleerd.</string>
+ <string name="passphrases_do_not_match">De wachtwoorden komen niet overeen.</string>
+ <string name="passphrase_must_not_be_empty">Vul een wachtwoord in.</string>
+ <string name="passphrase_for_symmetric_encryption">Symmetrische versleuteling.</string>
+ <string name="passphrase_for">Voer het wachtwoord in voor \'%s\'</string>
+ <string name="file_delete_confirmation">Weer u zeker dat u het volgende wilt verwijderen:\n%s?</string>
+ <string name="file_delete_successful">Succesvol verwijderd.</string>
+ <string name="no_file_selected">Selecteer eerst een bestand.</string>
+ <string name="encrypt_sign_successful">Succesvol gesigneerd en/of gecodeerd.</string>
+ <string name="encrypt_sign_clipboard_successful">Succesvol gesigneerd en/of gecodeerd naar klembord.</string>
+ <string name="enter_passphrase_twice">Voer het wachtwoord tweemaal in.</string>
+ <string name="select_encryption_key">Selecteer ten minste één versleutelingssleutel.</string>
+ <string name="select_encryption_or_signature_key">Selecter ten minste één versleutelings-/ondertekeningssleutel.</string>
+ <string name="specify_file_to_encrypt_to">Specifieer naar welk bestand er gecodeerd moet worden.\nWAARSCHUWING: Bestand zal vervangen worden als het bestaat.</string>
+ <string name="specify_file_to_decrypt_to">Specifieer naar welk bestand gedecodeerd moet worden.\nWAARSCHUWING: Bestand zal vervangen worden als het bestaat.</string>
+ <string name="specify_file_to_export_to">Specifieer naar welk bestand er geëxporteerd moet worden.\nWAARSCHUWING: Bestand zal vervangen worden als het bestaat.</string>
+ <string name="key_deletion_confirmation_multi">Weet u zeker dat u alle weergegeven publieke sleutels wilt verwijderen?\nU kunt dit niet ongedaan maken!</string>
+ <string name="secret_key_deletion_confirmation">Weet u zeker dat u de privésleutel \'%s\' wilt verwijderen?\nDit kan niet ongedaan worden gemaakt.</string>
+ <string name="ask_save_changed_key">U heeft veranderingen aangebracht tot de sleutelring, wilt u deze opslaan?</string>
+ <string name="ask_empty_id_ok">U heeft een lege identiteit toegevoegd, weet u zeker dat u wilt doorgaan?</string>
+ <string name="public_key_deletetion_confirmation">Wilt u echt de publieke sleutel \'%s\' verwijderen?\nDit kunt u niet ongedaan maken!</string>
+ <string name="also_export_secret_keys">Ook geheime sleutels exporteren?</string>
+ <plurals name="keys_added_and_updated_1">
+ <item quantity="one">Succesvol %d sleutel toegevoegd</item>
+ <item quantity="other">Succesvol %d sleutels toegevoegd</item>
+ </plurals>
+ <plurals name="keys_added_and_updated_2">
+ <item quantity="one">en %d sleutel bijgewerkt.</item>
+ <item quantity="other">en %d sleutels bijgewerkt.</item>
+ </plurals>
+ <plurals name="keys_added">
+ <item quantity="one">Succesvol %d sleutel toegevoegd.</item>
+ <item quantity="other">Succesvol %d sleutels toegevoegd.</item>
+ </plurals>
+ <plurals name="keys_updated">
+ <item quantity="one">Succesvol %d sleutel bijgewerkt.</item>
+ <item quantity="other">Succesvol %d sleutels bijgewerkt.</item>
+ </plurals>
+ <string name="no_keys_added_or_updated">Geen sleutels toegevoegd of bijgewerkt.</string>
+ <string name="key_exported">1 sleutel succesvol geëxporteerd.</string>
+ <string name="keys_exported">Succesvol %d sleutels geëxporteerd.</string>
+ <string name="no_keys_exported">Geen sleutels geëxporteerd.</string>
+ <string name="key_creation_el_gamal_info">Opmerking: alleen subsleutels ondersteunen ElGamal.</string>
+ <string name="key_creation_weak_rsa_info">Opmerking: RSA sleutel met lengte 1024-bit en minder genereren wordt als onveilig beschouwd en is uitgeschakeld voor het genereren van nieuwe sleutels.</string>
+ <string name="key_not_found">Kan de sleutel %08X niet vinden.</string>
+ <plurals name="keys_found">
+ <item quantity="one">%d sleutel gevonden.</item>
+ <item quantity="other">%d sleutels gevonden.</item>
+ </plurals>
+ <plurals name="bad_keys_encountered">
+ <item quantity="one">%d slechte geheime sleutel genegeerd. Misschien heeft u geëxporteerd met de optie\n --export-secret-subkeys\nZorg ervoor dat u in plaats daarvan met\n --export-secret-keys\nexporteert.</item>
+ <item quantity="other">%d slechte geheime sleutels genegeerd. Misschien heeft u geëxporteerd met de optie\n --export-secret-subkeys\nZorg ervoor dat u in plaats daarvan met\n --export-secret-keys\nexporteert.</item>
+ </plurals>
+ <string name="key_send_success">Succesvol sleutel naar server geüpload</string>
+ <string name="key_certify_success">Succesvol identiteiten gecertificeerd</string>
+ <string name="list_empty">Lijst is leeg</string>
+ <string name="nfc_successful">Succesvol sleutel verstuurd met NFC Beam!</string>
+ <string name="key_copied_to_clipboard">Sleutel is gekopieerd naar het klembord!</string>
+ <string name="fingerprint_copied_to_clipboard">Sleutel is gekopieerd naar het klembord!</string>
+ <string name="key_has_already_been_certified">Sleutel is al gecertificeerd!</string>
+ <string name="select_key_to_certify">Selecteer een sleutel die gebruikt moet worden voor certificatie!</string>
+ <string name="key_too_big_for_sharing">Sleutel is te groot om op deze manier gedeeld te worden!</string>
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <string name="error_file_delete_failed">verwijderen \'%s\' mislukt</string>
+ <string name="error_file_not_found">bestand niet gevonden</string>
+ <string name="error_no_secret_key_found">geen geschikte privésleutel gevonden</string>
+ <string name="error_external_storage_not_ready">externe opslag niet gereed</string>
+ <string name="error_key_size_minimum512bit">sleutelgrootte moet minstens 512-bits zijn</string>
+ <string name="error_master_key_must_not_be_el_gamal">de hoofdsleutel kan geen ElGamal-sleutel zijn</string>
+ <string name="error_unknown_algorithm_choice">onbekende algoritmekeuze</string>
+ <string name="error_user_id_no_email">geen e-mail gevonden</string>
+ <string name="error_key_needs_a_user_id">minstens een identiteit vereist</string>
+ <string name="error_main_user_id_must_not_be_empty">primaire identiteit mag niet leeg zijn</string>
+ <string name="error_key_needs_master_key">ten minste een hoofdsleutel is vereist</string>
+ <string name="error_no_signature_passphrase">geen wachtwoord opgegeven</string>
+ <string name="error_no_signature_key">geen ondertekeningssleutel opgegeven</string>
+ <string name="error_invalid_data">geen geldige versleutelingsgegevens</string>
+ <string name="error_integrity_check_failed">integriteitcheck niet geslaagd! Data is bewerkt!</string>
+ <string name="error_wrong_passphrase">wachtwoord verekerd</string>
+ <string name="error_saving_keys">fout bij het opslaan van bepaalde sleutels</string>
+ <string name="error_could_not_extract_private_key">kan privésleutel niet uitpakken</string>
+ <string name="error_expiry_must_come_after_creation">vervaldatum moet na de aanmaakdatum zijn</string>
+ <!--errors without preceeding Error:-->
+ <string name="error_only_files_are_supported">Directe binaire data zonder eigenlijke bestand in bestandssysteem wordt niet ondersteund.</string>
+ <string name="error_jelly_bean_needed">U heeft minstens Android 4.1 nodig om Androids NFC Beam eigenschap te gebruiken!</string>
+ <string name="error_nfc_needed">Uw apparaat biedt geen ondersteuning voor NFC</string>
+ <string name="error_nothing_import">Niets te importeren</string>
+ <string name="error_import_file_no_content">Bestand heeft geen inhoud</string>
+ <string name="error_generic_report_bug">Een algemene fout is opgetreden, maak alstublieft een nieuwe bug report voor OpenKeychain.</string>
+ <plurals name="error_import_non_pgp_part">
+ <item quantity="one">Deel van het geladen bestand is geldig OpenPGP object maar niet een OpenPGP sleutel</item>
+ <item quantity="other">Delen van het geladen bestand zijn geldige OpenPGP objecten maar niet OpenPGP sleutels</item>
+ </plurals>
+ <string name="error_change_something_first">U moet veranderingen maken aan de sleutelring voor u het kunt opslaan.</string>
+ <!--results shown after decryption/verification-->
+ <string name="decrypt_result_invalid_signature">Ongeldige handtekening!</string>
+ <string name="decrypt_result_signature_unknown_pub_key">Onbekende publieke sleutel</string>
+ <string name="decrypt_result_signature_uncertified">Geldige handtekening (ongecertificeerd)</string>
+ <string name="decrypt_result_signature_certified">Geldige handtekening (gecertificeerd)</string>
+ <string name="decrypt_result_decrypted">Succesvol gedecodeerd</string>
+ <string name="decrypt_result_decrypted_unknown_pub_key">Succesvol gedecodeerd maar onbekende publieke sleutel</string>
+ <string name="decrypt_result_decrypted_and_signature_uncertified">Succesvol gedecodeerd en geldige handtekening (ongecertificeerd)</string>
+ <string name="decrypt_result_decrypted_and_signature_certified">Succesvol gedecodeerd en geldige handtekening (gecertificeerd)</string>
+ <!--progress dialogs, usually ending in '…'-->
+ <string name="progress_done">Gereed.</string>
+ <string name="progress_cancel">Annuleren</string>
+ <string name="progress_saving">opslaan...</string>
+ <string name="progress_importing">importeren...</string>
+ <string name="progress_exporting">exporteren...</string>
+ <string name="progress_building_key">sleutel maken...</string>
+ <string name="progress_certifying_master_key">hoofdsleutel certificeren...</string>
+ <string name="progress_building_master_key">hoofdsleutelbos maken...</string>
+ <string name="progress_adding_sub_keys">sub-sleutels toevoegen...</string>
+ <string name="progress_saving_key_ring">sleutel opslaan...</string>
+ <plurals name="progress_exporting_key">
+ <item quantity="one">sleutel exporteren...</item>
+ <item quantity="other">sleutels exporteren...</item>
+ </plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">sleutel genereren, dit kan tot 3 minuten duren...</item>
+ <item quantity="other">sleutels genereren, dit kan tot 3 minuten duren...</item>
+ </plurals>
+ <string name="progress_extracting_signature_key">ondertekeningssleutel uitpakken...</string>
+ <string name="progress_extracting_key">sleutel uitpakken...</string>
+ <string name="progress_preparing_streams">streams voorbereiden...</string>
+ <string name="progress_encrypting">gegevens versleutelen...</string>
+ <string name="progress_decrypting">gegevens ontsleutelen...</string>
+ <string name="progress_preparing_signature">handtekening voorbereiden...</string>
+ <string name="progress_generating_signature">handtekening genereren...</string>
+ <string name="progress_processing_signature">handtekening verwerken...</string>
+ <string name="progress_verifying_signature">handtekening verifiëren...</string>
+ <string name="progress_signing">ondertekenen...</string>
+ <string name="progress_reading_data">gegevens lezen...</string>
+ <string name="progress_finding_key">sleutel opzoeken...</string>
+ <string name="progress_decompressing_data">gegevens decomprimeren...</string>
+ <string name="progress_verifying_integrity">integriteit verifiëren...</string>
+ <string name="progress_deleting_securely">\'%s\' veilig verwijderen...</string>
+ <!--action strings-->
+ <string name="hint_public_keys">Publieke sleutels zoeken</string>
+ <string name="hint_secret_keys">Privésleutels zoeken</string>
+ <string name="action_share_key_with">Sleutel delen met...</string>
+ <string name="hint_keybase_search">Doorzoek Keybase.io</string>
+ <!--key bit length selections-->
+ <string name="key_size_512">512</string>
+ <string name="key_size_768">768</string>
+ <string name="key_size_1024">1024</string>
+ <string name="key_size_1536">1536</string>
+ <string name="key_size_2048">2048</string>
+ <string name="key_size_3072">3072</string>
+ <string name="key_size_4096">4096</string>
+ <string name="key_size_8192">8192</string>
+ <string name="key_size_custom">Aangepaste sleutelgrootte</string>
+ <string name="key_size_custom_info">Typ aangepaste sleutellengte (in bits):</string>
+ <string name="key_size_custom_info_rsa">RSA sleutel lengte moet groter zijn dan 1024 en maximaal 8192. Het moet ook deelbaar zijn door 8.</string>
+ <string name="key_size_custom_info_dsa">DSA sleutellengte moet minstens 512 zijn en maximaal 1024. Het moet ook deelbaar zijn door 64.</string>
+ <!--compression-->
+ <string name="compression_fast">snel</string>
+ <string name="compression_very_slow">zeer langzaam</string>
+ <!--Help-->
+ <string name="help_tab_start">Beginnen</string>
+ <string name="help_tab_faq">FAQ</string>
+ <string name="help_tab_wot">Web van Vertrouwen</string>
+ <string name="help_tab_nfc_beam">NFC Beam</string>
+ <string name="help_tab_changelog">Lijst van wijzigingen</string>
+ <string name="help_tab_about">Over</string>
+ <string name="help_about_version">Versie:</string>
+ <!--Import-->
+ <string name="import_import">Geselecteerde sleutels importeren</string>
+ <string name="import_from_clipboard">Importeren uit klembord</string>
+ <plurals name="import_qr_code_missing">
+ <item quantity="one">Missende QR code met ID %s</item>
+ <item quantity="other">Missende QR Codes met ID\'s %s</item>
+ </plurals>
+ <string name="import_qr_code_start_with_one">Begin met QR Code met ID 1</string>
+ <string name="import_qr_code_wrong">QR-code ongeldig. Probeer het opnieuw</string>
+ <string name="import_qr_code_finished">QR-code gescand</string>
+ <string name="import_qr_code_too_short_fingerprint">Vingerafdruk is te kort (&lt; 16 tekens)</string>
+ <string name="import_qr_scan_button">Scan QR Code met \'Barcode Scanner\'</string>
+ <string name="import_nfc_text">Om sleutels via NFC te ontvangen moet het apparaat ontgrendeld zijn.</string>
+ <string name="import_nfc_help_button">Help</string>
+ <string name="import_clipboard_button">Verkrijg sleutel uit klembord</string>
+ <string name="import_keybase_button">Verkrijg sleutel uit Keybase.io</string>
+ <!--Intent labels-->
+ <string name="intent_decrypt_file">Decodeer bestand met OpenKeychain</string>
+ <string name="intent_import_key">Importeer Sleutel met OpenKeychain</string>
+ <string name="intent_send_encrypt">Codeer met OpenKeychain</string>
+ <string name="intent_send_decrypt">Decodeer met OpenKeychain</string>
+ <!--Remote API-->
+ <string name="api_no_apps">Geen geregistreerde applicaties!\n\nEen lijst van ondersteunde derde-partij applicaties kan gevonden worden in \'Help\'!</string>
+ <string name="api_settings_show_info">Toon geavanceerde informatie</string>
+ <string name="api_settings_hide_info">Verberg geavanceerde informatie</string>
+ <string name="api_settings_show_advanced">Toon geavanceerde instellingen</string>
+ <string name="api_settings_hide_advanced">Verberg geavanceerde instellingen</string>
+ <string name="api_settings_no_key">Geen sleutel geselecteerd</string>
+ <string name="api_settings_select_key">Sleutel selecteren</string>
+ <string name="api_settings_create_key">Maak nieuwe sleutel voor dit account</string>
+ <string name="api_settings_save">Opslaan</string>
+ <string name="api_settings_cancel">Annuleren</string>
+ <string name="api_settings_revoke">Toegang herroepen</string>
+ <string name="api_settings_start">Start applicatie</string>
+ <string name="api_settings_delete_account">Verwijder account</string>
+ <string name="api_settings_package_name">Pakketnaam</string>
+ <string name="api_settings_package_signature">SHA-256 van Pakkethandtekening</string>
+ <string name="api_settings_accounts">Accounts</string>
+ <string name="api_settings_accounts_empty">Geen accounts verbonden aan deze applicatie.</string>
+ <string name="api_create_account_text">Deze applicatie vraagt de aanmaak van een nieuw account aan. Selecteer een bestaande privésleutel of maak een nieuwe.\nApplicaties zijn gelimiteerd tot het gebruik van sleutels die u hier selecteert!</string>
+ <string name="api_register_text">De weergegeven applicatie vraagt toegang tot OpenKeychain.\nSta toegang toe?\n\nWAARSCHUWING: Als u niet weet waarom dit scherm verscheen, sta dan geen toegang toe! U kunt toegang later weghalen door het \'Geregistreerde Applicaties\' scherm te gebruiken.</string>
+ <string name="api_register_allow">Toegang toestaan</string>
+ <string name="api_register_disallow">Toegang weigeren</string>
+ <string name="api_register_error_select_key">Selecteert u a.u.b. een sleutel</string>
+ <string name="api_select_pub_keys_missing_text">Geen publieke sleutels zijn gevonden voor deze identiteiten:</string>
+ <string name="api_select_pub_keys_dublicates_text">Meer dan een publieke sleutel bestaat voor deze identiteiten:</string>
+ <string name="api_select_pub_keys_text">Bekijkt u a.u.b. de ontvangers</string>
+ <string name="api_error_wrong_signature">Handtekening check mislukt! Hebt u deze app van een andere bron geïnstalleerd? Als u zeker weet dat dit geen aanval is, haal dan de registratie van deze app in OpenKeychain weg en registreer de app opnieuw.</string>
+ <!--Share-->
+ <string name="share_qr_code_dialog_title">Delen met QR-code</string>
+ <string name="share_qr_code_dialog_start">U gaat door alle QR-codes met \'Volgende\', en scant ze een voor een.</string>
+ <string name="share_qr_code_dialog_fingerprint_text">Vingerafdruk:</string>
+ <string name="share_qr_code_dialog_progress">QR Code met ID %1$d van %2$d</string>
+ <string name="share_nfc_dialog">Deel met NFC</string>
+ <!--Key list-->
+ <plurals name="key_list_selected_keys">
+ <item quantity="one">1 sleutel geselecteerd.</item>
+ <item quantity="other">%d sleutels geselecteerd.</item>
+ </plurals>
+ <string name="key_list_empty_text1">Nog geen sleutels beschikbaar...</string>
+ <string name="key_list_empty_text2">U kunt beginnen door</string>
+ <string name="key_list_empty_text3">of</string>
+ <string name="key_list_empty_button_create">uw eigen sleutel aanmaken</string>
+ <string name="key_list_empty_button_import">sleutels importeren.</string>
+ <!--Key view-->
+ <string name="key_view_action_edit">Sleutel bewerken</string>
+ <string name="key_view_action_encrypt">Codeer met deze sleutel</string>
+ <string name="key_view_action_certify">Certifiëer identiteiten</string>
+ <string name="key_view_action_share_with">Delen met...</string>
+ <string name="key_view_action_share_nfc">Delen via NFC door de apparaten met de achterkant tegen elkaar te houden</string>
+ <string name="key_view_tab_main">Hoofd Info</string>
+ <string name="key_view_tab_share">Delen</string>
+ <string name="key_view_tab_keys">Subsleutels</string>
+ <string name="key_view_tab_certs">Certificaten</string>
+ <!--Navigation Drawer-->
+ <string name="nav_keys">Sleutels</string>
+ <string name="nav_encrypt">Ondertekenen en Versleutelen</string>
+ <string name="nav_decrypt">Decoderen en Verifiëren</string>
+ <string name="nav_import">Importeer Sleutels</string>
+ <string name="nav_apps">Geregistreerde apps</string>
+ <string name="drawer_open">Open navigatiemenu</string>
+ <string name="drawer_close">Sluit navigatiemenu</string>
+ <string name="edit">Bewerken</string>
+ <string name="my_keys">Mijn Sleutels</string>
+ <string name="label_secret_key">Geheime Sleutel</string>
+ <string name="secret_key_yes">beschikbaar</string>
+ <string name="secret_key_no">onbeschikbaar</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">Schrijf bericht hier om te versleutelen en/of ondertekenen...</string>
+ <string name="decrypt_content_edit_text_hint">Voer cijfertekst hier in om te decoderen en/of verifiëren...</string>
+ <!--certs-->
+ <string name="cert_default">standaard</string>
+ <string name="cert_none">geen</string>
+ <string name="cert_casual">eenvoudig</string>
+ <string name="cert_positive">positief</string>
+ <string name="cert_revoke">ingetrokken</string>
+ <string name="cert_verify_ok">ok</string>
+ <string name="cert_verify_failed">mislukt!</string>
+ <string name="cert_verify_error">fout!</string>
+ <string name="cert_verify_unavailable">sleutel onbeschikbaar</string>
+ <!--unsorted-->
+ <string name="section_certifier_id">Certificeer</string>
+ <string name="section_cert">Certificaat Details</string>
+ <string name="label_user_id">Identiteit</string>
+ <string name="unknown_uid">&lt;onbekend&gt;</string>
+ <string name="empty_certs">Geen certificaten voor deze sleutel</string>
+ <string name="section_uids_to_certify">Identiteiten om te certificeren</string>
+ <string name="label_revocation">Intrek Reden</string>
+ <string name="label_verify_status">Verificatie Status</string>
+ <string name="label_cert_type">Type</string>
+ <string name="error_key_not_found">Sleutel niet gevonden!</string>
+ <string name="error_key_processing">Fout bij verwerken sleutel!</string>
+ <string name="no_subkey">subsleutel onbeschikbaar</string>
+ <string name="key_stripped">gestript</string>
+ <string name="secret_cannot_multiple">Geheime sleutels kunnen alleen individueel verwijderd worden!</string>
+ <string name="title_view_cert">Toon Certificaat Details</string>
+ <string name="unknown_algorithm">onbekend</string>
+ <string name="can_sign_not">kan niet ondertekenen</string>
+ <string name="error_no_encrypt_subkey">Geen codeer-subsleutel beschikbaar!</string>
+</resources>
diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml
index 2f327c9bd..d1b7de393 100644
--- a/OpenKeychain/src/main/res/values-pl/strings.xml
+++ b/OpenKeychain/src/main/res/values-pl/strings.xml
@@ -259,9 +259,6 @@
<string name="error_jelly_bean_needed">Potrzebujesz Androida 4.1 aby korzystać z Android NFC Beam</string>
<string name="error_nfc_needed">NCF jest niedostępne na twoim urządzeniu</string>
<string name="error_nothing_import">Nie ma nic do zaimportowania!</string>
- <string name="error_keyserver_insufficient_query">Niewystarczające zapytanie do serwera</string>
- <string name="error_keyserver_query">Odpytywanie serwera zakończone niepowodzeniem</string>
- <string name="error_keyserver_too_many_responses">Zbyt wiele możliwych kluczy. Proszę zweryfikuj swoje zapytanie!</string>
<string name="error_import_file_no_content">Plik jest pusty</string>
<string name="error_generic_report_bug">Wystąpił błąd ogólny, proszę zgłoś go autorom OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index b51bcbde2..0becea0bc 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -13,6 +13,10 @@
<string name="title_key_server_preference">Настройки сервера ключей</string>
<string name="title_change_passphrase">Изменить пароль</string>
<string name="title_set_passphrase">Задать пароль</string>
+ <string name="title_share_with">Отправить...</string>
+ <string name="title_share_fingerprint_with">Отправить отпечаток...</string>
+ <string name="title_share_key">Отправить ключ...</string>
+ <string name="title_share_file">Отправить файл...</string>
<string name="title_encrypt_to_file">Зашифровать в файл</string>
<string name="title_decrypt_to_file">Расшифровать в файл</string>
<string name="title_import_keys">Импорт ключей</string>
@@ -20,22 +24,32 @@
<string name="title_export_keys">Экспорт ключей</string>
<string name="title_key_not_found">Ключ не найден</string>
<string name="title_send_key">Загрузить на сервер ключей</string>
+ <string name="title_certify_key">Сертифицировать</string>
<string name="title_key_details">Сведения о ключе</string>
<string name="title_help">Помощь</string>
<!--section-->
+ <string name="section_user_ids">Идентификаторы</string>
+ <string name="section_keys">Доп. ключи</string>
<string name="section_general">Приложение</string>
<string name="section_defaults">Алгоритмы</string>
<string name="section_advanced">Дополнительно</string>
<string name="section_master_key">Основной ключ</string>
+ <string name="section_master_user_id">Основной идентификатор</string>
<string name="section_actions">Действия</string>
+ <string name="section_share_key">Ключ</string>
<string name="section_certification_key">Ваш ключ для сертификации</string>
<string name="section_upload_key">Загрузить ключ</string>
<string name="section_key_server">Сервер ключей</string>
<string name="section_encrypt_and_or_sign">Зашифровать и/или Подписать</string>
<string name="section_decrypt_verify">Расшифровать и проверить</string>
+ <string name="section_fingerprint">Отпечаток ключа</string>
+ <string name="section_key_to_certify">Сертифицируемый ключ</string>
<!--button-->
<string name="btn_certify">Сертифицировать</string>
+ <string name="btn_decrypt_verify_file">Расшифровать, проверить и сохранить файл</string>
+ <string name="btn_decrypt_verify_message">Расшифровать и проверить сообщение</string>
<string name="btn_decrypt_verify_clipboard">Из буфера обмена</string>
+ <string name="btn_encrypt_file">Зашифровать и сохранить файл</string>
<string name="btn_save">Сохранить</string>
<string name="btn_do_not_save">Отмена</string>
<string name="btn_delete">Удалить</string>
@@ -51,6 +65,8 @@
<string name="btn_lookup_key">Найти ключ</string>
<string name="btn_encryption_advanced_settings_show">Показать расширенные настройки</string>
<string name="btn_encryption_advanced_settings_hide">Скрыть расширенные настройки</string>
+ <string name="btn_share_encrypted_signed">Отправить зашифрованное/подписанное сообщение...</string>
+ <string name="btn_view_cert_key">Просмотр ключа</string>
<!--menu-->
<string name="menu_preferences">Настройки</string>
<string name="menu_help">Помощь</string>
@@ -63,6 +79,7 @@
<string name="menu_create_key_expert">Создать ключ (эксперт)</string>
<string name="menu_search">Поиск</string>
<string name="menu_import_from_key_server">Сервер ключей</string>
+ <string name="menu_import_from_keybase">Импорт с сервера Keybase.io</string>
<string name="menu_key_server">Сервер ключей...</string>
<string name="menu_update_key">Обновить с сервера ключей</string>
<string name="menu_export_key_to_server">Загрузить на сервер ключей</string>
@@ -108,6 +125,7 @@
<string name="label_expiry">Годен до...</string>
<string name="label_usage">Применение</string>
<string name="label_key_size">Размер ключа</string>
+ <string name="label_main_user_id">Основной идентификатор</string>
<string name="label_name">Имя</string>
<string name="label_comment">Комментарий</string>
<string name="label_email">Email</string>
@@ -174,6 +192,8 @@
<string name="file_delete_confirmation">Вы уверены, что хотите удалить\n%s ?</string>
<string name="file_delete_successful">Удалено.</string>
<string name="no_file_selected">Сначала выберите файл.</string>
+ <string name="encrypt_sign_successful">Успешно подписано и/или зашифровано.</string>
+ <string name="encrypt_sign_clipboard_successful">Успешно подписано и/или зашифровано в буфер обмена.</string>
<string name="enter_passphrase_twice">Дважды введите пароль.</string>
<string name="select_encryption_key">Укажите хотя бы один ключ.</string>
<string name="select_encryption_or_signature_key">Выберите хотя бы один ключ для шифрования или подписи.</string>
@@ -182,6 +202,8 @@
<string name="specify_file_to_export_to">Пожалуйста, выберите файл для экспорта.\nВНИМАНИЕ! Если файл существует, он будет перезаписан.</string>
<string name="key_deletion_confirmation_multi">Вы уверены, что хотите удалить выбранные ключи?\nЭто действие нельзя отменить!</string>
<string name="secret_key_deletion_confirmation">Вы уверены, что ходите удалить СЕКРЕТНЫЙ ключ \'%s\'?\nЭто действие нельзя отменить!</string>
+ <string name="ask_save_changed_key">Изменения внесены. Сохранить?</string>
+ <string name="ask_empty_id_ok">Вы добавили пустой идентификатор. Вы уверены, что хотите продолжить?</string>
<string name="public_key_deletetion_confirmation">Вы правда хотите удалить публичный ключ \'%s\'?\nЭто действие нельзя отменить!</string>
<string name="also_export_secret_keys">Экспортировать секретные ключи?</string>
<plurals name="keys_added_and_updated_1">
@@ -222,10 +244,13 @@
<item quantity="other">%d плохих секретных ключей проигнорировано. Возможно, вы экспортируете с параметром\n--export-secret-subkeys\nВместо этого используйте\n--export-secret-keys\n</item>
</plurals>
<string name="key_send_success">Ключ успешно загружен на сервер</string>
+ <string name="key_certify_success">Подписанные идентификаторы</string>
<string name="list_empty">Список пуст!</string>
<string name="nfc_successful">Ключ успешно передан через NFC!</string>
<string name="key_copied_to_clipboard">Ключ скопирован в буфер обмена!</string>
+ <string name="fingerprint_copied_to_clipboard">Отпечаток ключа скопирован в буфер обмена!</string>
<string name="key_has_already_been_certified">Ключ уже был сертифицирован ранее!</string>
+ <string name="select_key_to_certify">Выберите ключ, используемый для сертификации!</string>
<string name="key_too_big_for_sharing">Ключ слишком большой для этого способа передачи!</string>
<!--errors
no punctuation, all lowercase,
@@ -238,6 +263,8 @@
<string name="error_master_key_must_not_be_el_gamal">ключ ElGamal не может быть основным</string>
<string name="error_unknown_algorithm_choice">выбран неизвестный алгоритм</string>
<string name="error_user_id_no_email">email не найден</string>
+ <string name="error_key_needs_a_user_id">необходим хотя бы один идентификатор</string>
+ <string name="error_main_user_id_must_not_be_empty">основной идентификатор не может быть пуст</string>
<string name="error_key_needs_master_key">необходим основной ключ</string>
<string name="error_no_signature_passphrase">пароль не задан</string>
<string name="error_no_signature_key">ключ для подписи не задан</string>
@@ -252,9 +279,9 @@
<string name="error_jelly_bean_needed">Для использования NFC Beam требуется Android 4.1+ !</string>
<string name="error_nfc_needed">Ваше устройство не поддерживает NFC!</string>
<string name="error_nothing_import">Нет данных для импорта!</string>
- <string name="error_keyserver_insufficient_query">Ограничение запроса сервера</string>
- <string name="error_keyserver_query">Сбой запроса сервера ключей</string>
- <string name="error_keyserver_too_many_responses">Слишком много возможных ключей. Уточните свой запрос!</string>
+ <string name="error_keyserver_insufficient_query">Запрос слишком короткий</string>
+ <string name="error_searching_keys">Ошибка поиска ключей на сервере</string>
+ <string name="error_keyserver_too_many_responses">Поиск ключа вернул слишком много вариантов; Пожалуйста, уточните запрос</string>
<string name="error_import_file_no_content">Файл пуст</string>
<string name="error_generic_report_bug">Выявлена ошибка. Пожалуйста, сообщите о ней разработчику.</string>
<plurals name="error_import_non_pgp_part">
@@ -262,12 +289,16 @@
<item quantity="few">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
<item quantity="other">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
</plurals>
+ <string name="error_change_something_first">Перед сохранением внесите изменения</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Неверная подпись!</string>
<string name="decrypt_result_signature_unknown_pub_key">Неизвестный ключ</string>
<string name="decrypt_result_signature_uncertified">Верная подпись (не сертифицирована)</string>
<string name="decrypt_result_signature_certified">Верная подпись (сертифицирована)</string>
<string name="decrypt_result_decrypted">Успешно расшифровано</string>
+ <string name="decrypt_result_decrypted_unknown_pub_key">Успешно расшифровано, но публичный ключ не известен</string>
+ <string name="decrypt_result_decrypted_and_signature_uncertified">Успешно расшифровано и подпись верна (не сертифицирована)</string>
+ <string name="decrypt_result_decrypted_and_signature_certified">Успешно расшифровано и подпись верна (сертифицирована)</string>
<!--progress dialogs, usually ending in '…'-->
<string name="progress_done">Готово.</string>
<string name="progress_cancel">Отмена</string>
@@ -308,6 +339,7 @@
<string name="hint_public_keys">Найти публичные ключи</string>
<string name="hint_secret_keys">Найти секретные ключи</string>
<string name="action_share_key_with">Отправить...</string>
+ <string name="hint_keybase_search">Поиск на сервере Keybase.io</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_768">768</string>
@@ -348,12 +380,14 @@
<string name="import_nfc_text">Разблокируйте устройство, что бы получить ключ через NFC.</string>
<string name="import_nfc_help_button">Помощь</string>
<string name="import_clipboard_button">Получить ключ из буфера</string>
+ <string name="import_keybase_button">Получить ключ с сервера Keybase.io</string>
<!--Intent labels-->
<string name="intent_decrypt_file">OpenKeychain: Расшифровать файл</string>
<string name="intent_import_key">OpenKeychain: Импортировать ключ</string>
<string name="intent_send_encrypt">OpenKeychain: Зашифровать</string>
<string name="intent_send_decrypt">OpenKeychain: Расшифровать</string>
<!--Remote API-->
+ <string name="api_no_apps">Нет связанных приложений!\n\nСписок приложений, поддерживающих интеграцию, доступен в разделе \'Помощь\'!</string>
<string name="api_settings_show_info">Показать подробную информацию</string>
<string name="api_settings_hide_info">Скрыть подробную информацию</string>
<string name="api_settings_show_advanced">Показать расширенные настройки</string>
@@ -364,6 +398,7 @@
<string name="api_settings_save">Сохранить</string>
<string name="api_settings_cancel">Отмена</string>
<string name="api_settings_revoke">Отозвать доступ</string>
+ <string name="api_settings_start">Запустить приложение</string>
<string name="api_settings_delete_account">Удалить аккаунт</string>
<string name="api_settings_package_name">Наименование пакета</string>
<string name="api_settings_package_signature">SHA-256 подписи пакета</string>
@@ -374,6 +409,8 @@
<string name="api_register_allow">Разрешить доступ</string>
<string name="api_register_disallow">Запретить доступ</string>
<string name="api_register_error_select_key">Пожалуйста, выберите ключ!</string>
+ <string name="api_select_pub_keys_missing_text">Для этих идентификаторов не найдены публичные ключи:</string>
+ <string name="api_select_pub_keys_dublicates_text">Для этих идентификаторов найдено более одного публичного ключа: </string>
<string name="api_select_pub_keys_text">Пожалуйста, проверьте получателей!</string>
<string name="api_error_wrong_signature">Проверка подписи пакета не удалась! Если вы установили программу из другого источника, отзовите для неё доступ к этой программе или обновите право доступа.</string>
<!--Share-->
@@ -394,7 +431,15 @@
<string name="key_list_empty_button_create">создать свой ключ</string>
<string name="key_list_empty_button_import">Импортировать ключи</string>
<!--Key view-->
+ <string name="key_view_action_edit">Изменить ключ</string>
<string name="key_view_action_encrypt">Зашифровать этим ключом</string>
+ <string name="key_view_action_certify">Сертифицировать</string>
+ <string name="key_view_action_share_with">Отправить...</string>
+ <string name="key_view_action_share_nfc">Отправить по NFC, удерживая устройства рядом</string>
+ <string name="key_view_tab_main">Основные данные</string>
+ <string name="key_view_tab_share">Отправить...</string>
+ <string name="key_view_tab_keys">Доп. ключи</string>
+ <string name="key_view_tab_certs">Сертификация</string>
<!--Navigation Drawer-->
<string name="nav_keys">Ключи</string>
<string name="nav_encrypt">Подписать и зашифровать</string>
@@ -414,14 +459,18 @@
<!--certs-->
<string name="cert_default">по умолчанию</string>
<string name="cert_none">нет</string>
+ <string name="cert_revoke">отозван</string>
<string name="cert_verify_ok">ok</string>
<string name="cert_verify_failed">сбой!</string>
<string name="cert_verify_error">ошибка!</string>
<string name="cert_verify_unavailable">ключ не доступен</string>
<!--unsorted-->
+ <string name="section_certifier_id">Кем подписан</string>
<string name="section_cert">Детали сертификации</string>
+ <string name="label_user_id">Идентификатор</string>
<string name="unknown_uid">&lt;неизв.&gt;</string>
<string name="empty_certs">Этот ключ не сертифицирован</string>
+ <string name="section_uids_to_certify">Идентификаторы</string>
<string name="label_revocation">Причина отзыва</string>
<string name="label_verify_status">Статус верификации</string>
<string name="label_cert_type">Тип</string>
@@ -431,4 +480,5 @@
<string name="secret_cannot_multiple">Секретные ключи можно удалять только по одному!</string>
<string name="title_view_cert">Просмотреть детали сертификации</string>
<string name="unknown_algorithm">неизв.</string>
+ <string name="error_no_encrypt_subkey">Нет доп. ключа для шифрования!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index f80db4b6c..8b12cdebe 100644
--- a/OpenKeychain/src/main/res/values-sl/strings.xml
+++ b/OpenKeychain/src/main/res/values-sl/strings.xml
@@ -291,9 +291,6 @@
<string name="error_jelly_bean_needed">Za uporabo storitve NFC Beam potrebujete najmanj Android 4.1!</string>
<string name="error_nfc_needed">NFC ni na voljo na vaši napravi!</string>
<string name="error_nothing_import">Ni česa uvoziti!</string>
- <string name="error_keyserver_insufficient_query">Pomanjkljiva poizvedba na strežniku</string>
- <string name="error_keyserver_query">Poizvedba na strežniku ni bila uspešna</string>
- <string name="error_keyserver_too_many_responses">Preveč možnih ključev. Redefinirajte svoje iskanje!</string>
<string name="error_import_file_no_content">Datoteka nima vsebine</string>
<string name="error_generic_report_bug">Pripetila se je splošna napaka, prosimo ustvarite poročilo o \'hrošču\'.</string>
<plurals name="error_import_non_pgp_part">
@@ -403,6 +400,7 @@
<string name="intent_send_encrypt">Šifriraj z OpenKeychain</string>
<string name="intent_send_decrypt">Dešifriraj z OpenKeychain</string>
<!--Remote API-->
+ <string name="api_no_apps">Ni nobene prijavljene aplikacije!\n\nSeznam podprtih aplikacij drugih avtorjev lahko najdete v Pomoči!</string>
<string name="api_settings_show_info">Pokaži dodatne informacije</string>
<string name="api_settings_hide_info">Skrij dodatne informacije</string>
<string name="api_settings_show_advanced">Pokaži napredne nastavitve</string>
@@ -413,6 +411,7 @@
<string name="api_settings_save">Shrani</string>
<string name="api_settings_cancel">Prekliči</string>
<string name="api_settings_revoke">Prekliči dostop</string>
+ <string name="api_settings_start">Zaženi aplikacijo</string>
<string name="api_settings_delete_account">Izbriši račun</string>
<string name="api_settings_package_name">Ime paketa</string>
<string name="api_settings_package_signature">SHA-256 podpisa paketa</string>
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index 54204a044..b27b6ffd3 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -13,6 +13,10 @@
<string name="title_key_server_preference">Налаштування сервера ключів</string>
<string name="title_change_passphrase">Змінити парольну фразу</string>
<string name="title_set_passphrase">Задати парольну фразу</string>
+ <string name="title_share_with">Поділитися через…</string>
+ <string name="title_share_fingerprint_with">Поділитися відбитком із…</string>
+ <string name="title_share_key">Поділитися ключем з…</string>
+ <string name="title_share_file">Поширити файл з…</string>
<string name="title_encrypt_to_file">Зашифрувати до файлу</string>
<string name="title_decrypt_to_file">Розшифрувати до файлу</string>
<string name="title_import_keys">Імпортувати ключі</string>
@@ -20,22 +24,32 @@
<string name="title_export_keys">Експортувати ключі</string>
<string name="title_key_not_found">Ключ не знайдено</string>
<string name="title_send_key">Завантажити на сервер ключів</string>
+ <string name="title_certify_key">Сертифікувати сутності</string>
<string name="title_key_details">Подробиці про ключ</string>
<string name="title_help">Довідка</string>
<!--section-->
+ <string name="section_user_ids">Сутності</string>
+ <string name="section_keys">Підключі</string>
<string name="section_general">Загальне</string>
<string name="section_defaults">Типове</string>
<string name="section_advanced">Додаткове</string>
<string name="section_master_key">Основний ключ</string>
+ <string name="section_master_user_id">Первинна сутність</string>
<string name="section_actions">Дії</string>
+ <string name="section_share_key">Цілий ключ</string>
<string name="section_certification_key">Ваш ключ використаний для сертифікації</string>
<string name="section_upload_key">Завантажити ключ</string>
<string name="section_key_server">Сервер ключів</string>
<string name="section_encrypt_and_or_sign">Шифрувати і/або підписати</string>
<string name="section_decrypt_verify">Розшифрувати і Перевірити</string>
+ <string name="section_fingerprint">Відбиток</string>
+ <string name="section_key_to_certify">Ключ для сертикації</string>
<!--button-->
<string name="btn_certify">Сертифікувати</string>
+ <string name="btn_decrypt_verify_file">Розшифрувати, перевірити та зберегти файл</string>
+ <string name="btn_decrypt_verify_message">Розшифрувати і перевірити повідомлення</string>
<string name="btn_decrypt_verify_clipboard">З буфера обміну</string>
+ <string name="btn_encrypt_file">Шифрувати і зберегти файл</string>
<string name="btn_save">Зберегти</string>
<string name="btn_do_not_save">Скасувати</string>
<string name="btn_delete">Вилучити</string>
@@ -51,6 +65,8 @@
<string name="btn_lookup_key">Шукати ключ</string>
<string name="btn_encryption_advanced_settings_show">Показати додаткові налаштування</string>
<string name="btn_encryption_advanced_settings_hide">Приховати додаткові налаштування</string>
+ <string name="btn_share_encrypted_signed">Поширити зашифроване/підписане повідомлення…</string>
+ <string name="btn_view_cert_key">Переглянути ключ сертифікації</string>
<!--menu-->
<string name="menu_preferences">Параметри</string>
<string name="menu_help">Довідка</string>
@@ -63,6 +79,7 @@
<string name="menu_create_key_expert">Створити ключ (експерт)</string>
<string name="menu_search">Пошук</string>
<string name="menu_import_from_key_server">Сервер ключів</string>
+ <string name="menu_import_from_keybase">Імпорт із Keybase.io</string>
<string name="menu_key_server">Сервер ключів…</string>
<string name="menu_update_key">Оновити з сервера ключів</string>
<string name="menu_export_key_to_server">Завантажити на сервер ключів</string>
@@ -102,11 +119,13 @@
<string name="label_message_compression">Стиснення повідомлення</string>
<string name="label_file_compression">Стиснення файлу</string>
<string name="label_force_v3_signature">Примусово старі підписи OpenPGPv3</string>
+ <string name="label_keyservers">Сервери ключів</string>
<string name="label_key_id">ІД ключа</string>
<string name="label_creation">Створення</string>
<string name="label_expiry">Закінчення</string>
<string name="label_usage">Використання</string>
<string name="label_key_size">Розмір ключа</string>
+ <string name="label_main_user_id">Первинна сутність</string>
<string name="label_name">Назва</string>
<string name="label_comment">Коментар</string>
<string name="label_email">Ел. пошта</string>
@@ -128,6 +147,16 @@
<string name="can_certify_not">не можна сертифікувати</string>
<string name="expired">закінчився</string>
<string name="revoked">скасовано</string>
+ <plurals name="n_keys">
+ <item quantity="one">1 ключ</item>
+ <item quantity="few">%d ключі</item>
+ <item quantity="other">%d ключів</item>
+ </plurals>
+ <plurals name="n_keyservers">
+ <item quantity="one">%d сервер ключів</item>
+ <item quantity="few">%d сервери ключів</item>
+ <item quantity="other">%d серверів ключів</item>
+ </plurals>
<string name="secret_key">Секретний ключ:</string>
<!--choice-->
<string name="choice_none">Жоден</string>
@@ -177,6 +206,7 @@
<string name="key_deletion_confirmation_multi">Ви справді хочете вилучити усі вибрані відкриті ключі?\nВи не зможете це відмінити!</string>
<string name="secret_key_deletion_confirmation">Ви справді хочете вилучити секретний ключ \'%s\'?\nВи не зможете це відмінити!</string>
<string name="ask_save_changed_key">Ви внесли зміни до в\'язки ключів, ви б хотіли. Волієте їх зберегти?</string>
+ <string name="ask_empty_id_ok">Ви вже додали порожню сутність. Ви справді хочете продовжити?</string>
<string name="public_key_deletetion_confirmation">Ви справді хочете вилучити відкритий ключ \'%s\'?\nВи не зможете це відмінити!</string>
<string name="also_export_secret_keys">Також експортувати секретні ключі?</string>
<plurals name="keys_added_and_updated_1">
@@ -217,8 +247,13 @@
<item quantity="other">%d поганих секретних ключів проігноровано. Можливо ви експортували з параметром\n --export-secret-subkeys\nЗробіть ваш експорт з \n --export-secret-keys\nнатомість.</item>
</plurals>
<string name="key_send_success">Успішно завантажено ключ на сервер</string>
+ <string name="key_certify_success">Успішно сертифіковані сутності</string>
<string name="list_empty">Цей список - порожній!</string>
+ <string name="nfc_successful">Успішно надіслано ключ через промінь NFC!</string>
<string name="key_copied_to_clipboard">Ключ вже скопійовано у буфер обміну!</string>
+ <string name="fingerprint_copied_to_clipboard">Відбиток вже скопійовано до буфера обміну!</string>
+ <string name="key_has_already_been_certified">Ключ вже сертифіковано!</string>
+ <string name="select_key_to_certify">Будь ласка, виберіть ключ для використання у сертифікації!</string>
<string name="key_too_big_for_sharing">Ключ надто великий для цього способу поширення!</string>
<!--errors
no punctuation, all lowercase,
@@ -231,6 +266,8 @@
<string name="error_master_key_must_not_be_el_gamal">основний ключ не може бути ключем ElGamal</string>
<string name="error_unknown_algorithm_choice">вибір невідомого алгоритму</string>
<string name="error_user_id_no_email">жодного листа не знайдено</string>
+ <string name="error_key_needs_a_user_id">хоча б одна сутність</string>
+ <string name="error_main_user_id_must_not_be_empty">первинна сутність повинна бути порожньою</string>
<string name="error_key_needs_master_key">потрібний хоча б один основний ключ</string>
<string name="error_no_signature_passphrase">не подано парольної фрази</string>
<string name="error_no_signature_key">не подано ключ підпису</string>
@@ -241,10 +278,13 @@
<string name="error_could_not_extract_private_key">не можна витягти секретний ключ</string>
<string name="error_expiry_must_come_after_creation">дата завершення дії має йти після дати створення</string>
<!--errors without preceeding Error:-->
+ <string name="error_only_files_are_supported">Пряма передача даних без використання файлу в пам\'яті пристрою не підтримується.</string>
+ <string name="error_jelly_bean_needed">Вам потрібний Android 4.1 для використання функції Androids NFC промінь!</string>
<string name="error_nfc_needed">NFC недоступний на вашому пристрої!</string>
<string name="error_nothing_import">Нема що імпортувати!</string>
- <string name="error_keyserver_insufficient_query">Запит обмеженого сервера</string>
- <string name="error_keyserver_query">Збій сервера ключа запиту</string>
+ <string name="error_keyserver_insufficient_query">Запит пошуку ключа надто короткий</string>
+ <string name="error_searching_keys">Невиправна помилка пошуку ключів в сервері</string>
+ <string name="error_keyserver_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string>
<string name="error_import_file_no_content">Файл не має вмісту</string>
<string name="error_generic_report_bug">Трапилася загальна помилка, будь ласка, створіть новий звіт про помилку для OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
@@ -302,6 +342,7 @@
<string name="hint_public_keys">Пошук публічних ключів</string>
<string name="hint_secret_keys">Пошук секретних ключів</string>
<string name="action_share_key_with">Поділитися ключем з…</string>
+ <string name="hint_keybase_search">Пошук Keybase.io</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_768">768</string>
@@ -342,12 +383,14 @@
<string name="import_nfc_text">Розблокуйте пристрій, щоб отримати ключ через NFC.</string>
<string name="import_nfc_help_button">Довідка</string>
<string name="import_clipboard_button">Отримати ключ з буфера обміну</string>
+ <string name="import_keybase_button">Отримати ключ із Keybase.io</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Розшифрувати файл з OpenKeychain</string>
<string name="intent_import_key">Імпортувати ключ з OpenKeychain</string>
<string name="intent_send_encrypt">Зашифрувати з OpenKeychain</string>
<string name="intent_send_decrypt">Розшифрувати з OpenKeychain</string>
<!--Remote API-->
+ <string name="api_no_apps">Немає зареєстрованих програм!\n\nСписок підтримуваних сторонніх програм можна знайти у „Довідці“!</string>
<string name="api_settings_show_info">Показати додаткову інформацію</string>
<string name="api_settings_hide_info">Приховати додаткову інформацію</string>
<string name="api_settings_show_advanced">Показати додаткові налаштування</string>
@@ -358,6 +401,7 @@
<string name="api_settings_save">Зберегти</string>
<string name="api_settings_cancel">Скасувати</string>
<string name="api_settings_revoke">Відкликати доступ</string>
+ <string name="api_settings_start">Запустити програму</string>
<string name="api_settings_delete_account">Видалити профіль</string>
<string name="api_settings_package_name">Назва пакунку</string>
<string name="api_settings_package_signature">SHA-256 підписку пакунку</string>
@@ -368,6 +412,8 @@
<string name="api_register_allow">Дозволити доступ</string>
<string name="api_register_disallow">Не дозволити доступ</string>
<string name="api_register_error_select_key">Будь ласка, виберіть ключ!</string>
+ <string name="api_select_pub_keys_missing_text">Немає публічних ключів для цих сутностей:</string>
+ <string name="api_select_pub_keys_dublicates_text">Наявно більше одного публічного ключа для цих сутностей:</string>
<string name="api_select_pub_keys_text">Будь ласка, перевірте список одержувачів!</string>
<string name="api_error_wrong_signature">Перевірка підпису пакету не вдалася! Може ви встановили програму з іншого джерела? Якщо ви впевнені, що це не атака, то відкличте реєстрацію програми у OpenKeychain та знову зареєструйте її.</string>
<!--Share-->
@@ -388,7 +434,17 @@
<string name="key_list_empty_button_create">створюється ваш власний ключ</string>
<string name="key_list_empty_button_import">імпортуюся ключі.</string>
<!--Key view-->
+ <string name="key_view_action_edit">Редагувати ключ</string>
+ <string name="key_view_action_encrypt">Шифрувати з цим ключем</string>
+ <string name="key_view_action_certify">Сертифікувати сутності</string>
+ <string name="key_view_action_share_with">Поділитися із…</string>
+ <string name="key_view_action_share_nfc">Поширити через NFC, тримаючи пристрої пліч-о-пліч</string>
+ <string name="key_view_tab_main">Основна інформація</string>
+ <string name="key_view_tab_share">Поділитися</string>
+ <string name="key_view_tab_keys">Підключі</string>
+ <string name="key_view_tab_certs">Сертифікати</string>
<!--Navigation Drawer-->
+ <string name="nav_keys">Ключі</string>
<string name="nav_encrypt">Підписати і зашифрувати</string>
<string name="nav_decrypt">Розшифрувати і Перевірити</string>
<string name="nav_import">Імпортувати ключі</string>
@@ -408,13 +464,18 @@
<string name="cert_none">жоден</string>
<string name="cert_casual">випадковий</string>
<string name="cert_positive">додатний</string>
+ <string name="cert_revoke">відкликано</string>
<string name="cert_verify_ok">Гаразд</string>
<string name="cert_verify_failed">Невдача!</string>
<string name="cert_verify_error">Помилка!</string>
<string name="cert_verify_unavailable">Недоступний ключ</string>
<!--unsorted-->
+ <string name="section_certifier_id">Ким підписаний</string>
<string name="section_cert">Дані сертифікату</string>
+ <string name="label_user_id">Сутність</string>
+ <string name="unknown_uid">&lt;невідомо&gt;</string>
<string name="empty_certs">Немає сертифікатів для цього ключа</string>
+ <string name="section_uids_to_certify">Сутності для сертифікації</string>
<string name="label_revocation">Причина відхилення</string>
<string name="label_verify_status">Стан перевірки</string>
<string name="label_cert_type">Тип</string>
@@ -423,4 +484,7 @@
<string name="no_subkey">підключ недоступний</string>
<string name="secret_cannot_multiple">Секретні ключі можна вилучити лише окремо!</string>
<string name="title_view_cert">Переглянути дані сертифікату</string>
+ <string name="unknown_algorithm">невідомий</string>
+ <string name="can_sign_not">не можна підписати</string>
+ <string name="error_no_encrypt_subkey">Жодний підключ шифрування недоступний!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 70b8616d4..09f76c675 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -517,6 +517,7 @@
<string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string>
<string name="can_sign_not">cannot sign</string>
+ <string name="error_encoding">Encoding error</string>
<string name="error_no_encrypt_subkey">No encryption subkey available!</string>
<string name="info_no_manual_account_creation">Do not create OpenKeychain-Accounts manually.
For more information, see Help.</string>