From 121f8aaca040cd54d8182f0ab9adba961bdfde6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 15 Sep 2013 15:29:38 +0200 Subject: Move pgp classes into subpackage --- .../keychain/helper/PgpConversionHelper.java | 139 -- .../keychain/helper/PgpHelper.java | 510 ------ .../keychain/helper/PgpMain.java | 1884 -------------------- .../keychain/helper/PgpToX509.java | 307 ---- .../keychain/pgp/PgpConversionHelper.java | 139 ++ .../sufficientlysecure/keychain/pgp/PgpHelper.java | 510 ++++++ .../sufficientlysecure/keychain/pgp/PgpMain.java | 1884 ++++++++++++++++++++ .../sufficientlysecure/keychain/pgp/PgpToX509.java | 307 ++++ .../keychain/provider/ProviderHelper.java | 6 +- .../keychain/service/KeychainIntentService.java | 6 +- .../keychain/service/PassphraseCacheService.java | 2 +- .../service/remote/AppSettingsFragment.java | 2 +- .../service/remote/ExtendedApiService.java | 6 +- .../keychain/service/remote/OpenPgpService.java | 2 +- .../service/remote/RemoteServiceActivity.java | 2 +- .../keychain/ui/DecryptActivity.java | 4 +- .../keychain/ui/EditKeyActivity.java | 8 +- .../keychain/ui/EncryptActivity.java | 4 +- .../keychain/ui/KeyListPublicFragment.java | 2 +- .../keychain/ui/KeyListSecretActivity.java | 2 +- .../keychain/ui/KeyServerQueryActivity.java | 2 +- .../keychain/ui/SignKeyActivity.java | 3 +- .../ui/dialog/DeleteKeyDialogFragment.java | 2 +- .../ui/dialog/LookupUnknownKeyDialogFragment.java | 2 +- .../ui/dialog/PassphraseDialogFragment.java | 7 +- .../keychain/ui/widget/ImportKeysListLoader.java | 2 +- .../keychain/ui/widget/KeyEditor.java | 2 +- .../keychain/ui/widget/KeyListAdapter.java | 2 +- .../keychain/ui/widget/SectionView.java | 2 +- .../keychain/ui/widget/SelectKeyCursorAdapter.java | 2 +- .../keychain/util/HkpKeyServer.java | 4 +- 31 files changed, 2877 insertions(+), 2879 deletions(-) delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpMain.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpToX509.java (limited to 'OpenPGP-Keychain/src') diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java deleted file mode 100644 index 566d4b1ad..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2012-2013 Dominik Schürmann - * - * 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 . - */ - -package org.sufficientlysecure.keychain.helper; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; - -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPObjectFactory; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.util.Log; - - -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 - * - * @param keysBytes - * @return - */ - public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { - PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); - ArrayList keys = new ArrayList(); - - @SuppressWarnings("unchecked") - Iterator itr = keyRing.getSecretKeys(); - while (itr.hasNext()) { - keys.add(itr.next()); - } - - return keys; - } - - /** - * Convert from byte[] to PGPSecretKey - * - * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle - * - * @param keysBytes - * @return - */ - public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { - PGPSecretKey key = BytesToPGPSecretKeyList(keyBytes).get(0); - - return key; - } - - /** - * Convert from ArrayList to byte[] - * - * @param keys - * @return - */ - public static byte[] PGPSecretKeyArrayListToBytes(ArrayList keys) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - for (PGPSecretKey key : keys) { - try { - key.encode(os); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); - } - } - - return os.toByteArray(); - } - - /** - * Convert from PGPSecretKey to byte[] - * - * @param keysBytes - * @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 keysBytes - * @return - */ - public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) { - try { - return keyRing.getEncoded(); - } catch (IOException e) { - Log.e(Constants.TAG, "Encoding failed", e); - - return null; - } - } - -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java deleted file mode 100644 index 3dd82c624..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.keychain.helper; - -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.Vector; - -import org.spongycastle.bcpg.sig.KeyFlags; -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.PGPSignatureSubpacketVector; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.IterableIterator; -import org.sufficientlysecure.keychain.util.Log; - -import android.content.Context; - -public class PgpHelper { - - public static Date getCreationDate(PGPPublicKey key) { - return key.getCreationTime(); - } - - public static Date getCreationDate(PGPSecretKey key) { - return key.getPublicKey().getCreationTime(); - } - - @SuppressWarnings("unchecked") - public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) { - if (keyRing == null) { - return null; - } - for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - if (key.isMasterKey()) { - return key; - } - } - - return null; - } - - @SuppressWarnings("unchecked") - public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) { - if (keyRing == null) { - return null; - } - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - if (key.isMasterKey()) { - return key; - } - } - - return null; - } - - @SuppressWarnings("unchecked") - public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) { - long cnt = 0; - if (keyRing == null) { - return null; - } - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - if (cnt == num) { - return key; - } - cnt++; - } - - return null; - } - - @SuppressWarnings("unchecked") - public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) { - Vector encryptKeys = new Vector(); - - for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - if (isEncryptionKey(key)) { - encryptKeys.add(key); - } - } - - return encryptKeys; - } - - @SuppressWarnings("unchecked") - public static Vector getSigningKeys(PGPSecretKeyRing keyRing) { - Vector signingKeys = new Vector(); - - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - if (isSigningKey(key)) { - signingKeys.add(key); - } - } - - return signingKeys; - } - - @SuppressWarnings("unchecked") - public static Vector getCertificationKeys(PGPSecretKeyRing keyRing) { - Vector signingKeys = new Vector(); - - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - if (isCertificationKey(key)) { - signingKeys.add(key); - } - } - - return signingKeys; - } - - public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) { - Vector usableKeys = new Vector(); - Vector encryptKeys = getEncryptKeys(keyRing); - PGPPublicKey masterKey = null; - for (int i = 0; i < encryptKeys.size(); ++i) { - PGPPublicKey key = encryptKeys.get(i); - if (!isExpired(key)) { - if (key.isMasterKey()) { - masterKey = key; - } else { - usableKeys.add(key); - } - } - } - if (masterKey != null) { - usableKeys.add(masterKey); - } - return usableKeys; - } - - 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; - } - - public static boolean isExpired(PGPSecretKey key) { - return isExpired(key.getPublicKey()); - } - - public static Vector getUsableCertificationKeys(PGPSecretKeyRing keyRing) { - Vector usableKeys = new Vector(); - Vector 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; - } - - public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) { - Vector usableKeys = new Vector(); - Vector 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 Date getExpiryDate(PGPPublicKey key) { - Date creationDate = getCreationDate(key); - if (key.getValidDays() == 0) { - // no expiry - return null; - } - Calendar calendar = GregorianCalendar.getInstance(); - calendar.setTime(creationDate); - calendar.add(Calendar.DATE, key.getValidDays()); - Date expiryDate = calendar.getTime(); - - return expiryDate; - } - - public static Date getExpiryDate(PGPSecretKey key) { - return getExpiryDate(key.getPublicKey()); - } - - public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, - masterKeyId); - if (keyRing == null) { - Log.e(Constants.TAG, "keyRing is null!"); - return null; - } - Vector encryptKeys = getUsableEncryptKeys(keyRing); - if (encryptKeys.size() == 0) { - Log.e(Constants.TAG, "encryptKeys is null!"); - return null; - } - return encryptKeys.get(0); - } - - public static PGPSecretKey getCertificationKey(Context context, long masterKeyId) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, - masterKeyId); - if (keyRing == null) { - return null; - } - Vector signingKeys = getUsableCertificationKeys(keyRing); - if (signingKeys.size() == 0) { - return null; - } - return signingKeys.get(0); - } - - public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, - masterKeyId); - if (keyRing == null) { - return null; - } - Vector signingKeys = getUsableSigningKeys(keyRing); - if (signingKeys.size() == 0) { - return null; - } - return signingKeys.get(0); - } - - @SuppressWarnings("unchecked") - public static String getMainUserId(PGPPublicKey key) { - for (String userId : new IterableIterator(key.getUserIDs())) { - return userId; - } - return null; - } - - @SuppressWarnings("unchecked") - public static String getMainUserId(PGPSecretKey key) { - for (String userId : new IterableIterator(key.getUserIDs())) { - return userId; - } - return null; - } - - public static String getMainUserIdSafe(Context context, PGPPublicKey key) { - String userId = getMainUserId(key); - if (userId == null || userId.equals("")) { - userId = context.getString(R.string.unknownUserId); - } - return userId; - } - - public static String getMainUserIdSafe(Context context, PGPSecretKey key) { - String userId = getMainUserId(key); - if (userId == null || userId.equals("")) { - userId = context.getString(R.string.unknownUserId); - } - return userId; - } - - @SuppressWarnings("unchecked") - public static boolean isEncryptionKey(PGPPublicKey key) { - if (!key.isEncryptionKey()) { - return false; - } - - if (key.getVersion() <= 3) { - // this must be true now - return key.isEncryptionKey(); - } - - // special cases - if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) { - return true; - } - - if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) { - return true; - } - - for (PGPSignature sig : new IterableIterator(key.getSignatures())) { - if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { - continue; - } - PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); - - if (hashed != null - && (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { - return true; - } - - PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); - - if (unhashed != null - && (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { - return true; - } - } - return false; - } - - public static boolean isEncryptionKey(PGPSecretKey key) { - return isEncryptionKey(key.getPublicKey()); - } - - @SuppressWarnings("unchecked") - public static boolean isSigningKey(PGPPublicKey key) { - if (key.getVersion() <= 3) { - return true; - } - - // special case - if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) { - return true; - } - - for (PGPSignature sig : new IterableIterator(key.getSignatures())) { - if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { - continue; - } - PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); - - if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { - return true; - } - - PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); - - if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { - return true; - } - } - - return false; - } - - public static boolean isSigningKey(PGPSecretKey key) { - return isSigningKey(key.getPublicKey()); - } - - @SuppressWarnings("unchecked") - public static boolean isCertificationKey(PGPPublicKey key) { - if (key.getVersion() <= 3) { - return true; - } - - for (PGPSignature sig : new IterableIterator(key.getSignatures())) { - if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { - continue; - } - PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); - - if (hashed != null && (hashed.getKeyFlags() & KeyFlags.CERTIFY_OTHER) != 0) { - return true; - } - - PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); - - if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.CERTIFY_OTHER) != 0) { - return true; - } - } - - return false; - } - - public static boolean isCertificationKey(PGPSecretKey key) { - return isCertificationKey(key.getPublicKey()); - } - - public static String getAlgorithmInfo(PGPPublicKey key) { - return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength()); - } - - public static String getAlgorithmInfo(PGPSecretKey key) { - return getAlgorithmInfo(key.getPublicKey()); - } - - public static String getAlgorithmInfo(int algorithm, int keySize) { - String algorithmStr = null; - - switch (algorithm) { - case PGPPublicKey.RSA_ENCRYPT: - case PGPPublicKey.RSA_GENERAL: - case PGPPublicKey.RSA_SIGN: { - algorithmStr = "RSA"; - break; - } - - case PGPPublicKey.DSA: { - algorithmStr = "DSA"; - break; - } - - case PGPPublicKey.ELGAMAL_ENCRYPT: - case PGPPublicKey.ELGAMAL_GENERAL: { - algorithmStr = "ElGamal"; - break; - } - - default: { - algorithmStr = "???"; - break; - } - } - return algorithmStr + ", " + keySize + "bit"; - } - - public static String convertFingerprintToHex(byte[] fp) { - String fingerPrint = ""; - for (int i = 0; i < fp.length; ++i) { - if (i != 0 && i % 10 == 0) { - fingerPrint += " "; - } else if (i != 0 && i % 2 == 0) { - fingerPrint += " "; - } - String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US); - while (chunk.length() < 2) { - chunk = "0" + chunk; - } - fingerPrint += chunk; - } - - return fingerPrint; - - } - - public static String getFingerPrint(Context context, long keyId) { - PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); - // if it is no public key get it from your own keys... - if (key == null) { - PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); - if (secretKey == null) { - Log.e(Constants.TAG, "Key could not be found!"); - return null; - } - key = secretKey.getPublicKey(); - } - - return convertFingerprintToHex(key.getFingerprint()); - } - - public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) { - return secretKey.isPrivateKeyEmpty(); - } - - public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) { - PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); - if (secretKey == null) { - Log.e(Constants.TAG, "Key could not be found!"); - return false; // could be a public key, assume it is not empty - } - return isSecretKeyPrivateEmpty(secretKey); - } - - public static String getSmallFingerPrint(long keyId) { - String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US); - while (fingerPrint.length() < 8) { - fingerPrint = "0" + fingerPrint; - } - return fingerPrint; - } - - public static String keyToHex(long keyId) { - return getSmallFingerPrint(keyId >> 32) + getSmallFingerPrint(keyId); - } - - public static long keyFromHex(String data) { - int len = data.length(); - String s2 = data.substring(len - 8); - String s1 = data.substring(0, len - 8); - return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16); - } - -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java deleted file mode 100644 index 71466ff22..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java +++ /dev/null @@ -1,1884 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.keychain.helper; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.Security; -import java.security.SignatureException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.regex.Pattern; - -import org.spongycastle.bcpg.ArmoredInputStream; -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.BCPGOutputStream; -import org.spongycastle.bcpg.CompressionAlgorithmTags; -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; -import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.spongycastle.jce.spec.ElGamalParameterSpec; -import org.spongycastle.openpgp.PGPCompressedData; -import org.spongycastle.openpgp.PGPCompressedDataGenerator; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.spongycastle.openpgp.PGPEncryptedDataGenerator; -import org.spongycastle.openpgp.PGPEncryptedDataList; -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPKeyPair; -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPKeyRingGenerator; -import org.spongycastle.openpgp.PGPLiteralData; -import org.spongycastle.openpgp.PGPLiteralDataGenerator; -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.PGPSignatureGenerator; -import org.spongycastle.openpgp.PGPSignatureList; -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.PBEDataDecryptorFactory; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; -import org.spongycastle.openpgp.operator.PGPDigestCalculator; -import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; -import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; -import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.util.HkpKeyServer; -import org.sufficientlysecure.keychain.util.InputData; -import org.sufficientlysecure.keychain.util.IterableIterator; -import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.PositionAwareInputStream; -import org.sufficientlysecure.keychain.util.Primes; -import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.os.Environment; - -/** - * TODO: - * - * - Separate this file into different helpers - * - */ -public class PgpMain { - - static { - // Define Java Security Provider to be Bouncy Castle - Security.insertProviderAt(new BouncyCastleProvider(), 1); - } - - // Not BC due to the use of Spongy Castle for Android - public static final String SC = BouncyCastleProvider.PROVIDER_NAME; - public static final String BOUNCY_CASTLE_PROVIDER_NAME = SC; - - private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] { - SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, - SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5, - SymmetricKeyAlgorithmTags.TRIPLE_DES }; - private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1, - HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 }; - private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] { - CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2, - CompressionAlgorithmTags.ZIP }; - - public static Pattern PGP_MESSAGE = Pattern.compile( - ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL); - - public static Pattern PGP_SIGNED_MESSAGE = Pattern - .compile( - ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", - Pattern.DOTALL); - - public static Pattern PGP_PUBLIC_KEY = Pattern.compile( - ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*", - Pattern.DOTALL); - - private static String mEditPassPhrase = null; - - public static class PgpGeneralException extends Exception { - static final long serialVersionUID = 0xf812773342L; - - public PgpGeneralException(String message) { - super(message); - } - } - - public static class NoAsymmetricEncryptionException extends Exception { - static final long serialVersionUID = 0xf812773343L; - - public NoAsymmetricEncryptionException() { - super(); - } - } - - public static void setEditPassPhrase(String passPhrase) { - mEditPassPhrase = passPhrase; - } - - public static String getEditPassPhrase() { - return mEditPassPhrase; - } - - public static void updateProgress(ProgressDialogUpdater progress, int message, int current, - int total) { - if (progress != null) { - progress.setProgress(message, current, total); - } - } - - public static void updateProgress(ProgressDialogUpdater progress, int current, int total) { - if (progress != null) { - progress.setProgress(current, total); - } - } - - /** - * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key - * when this key is the new masterkey. If a masterkey is supplied in the parameters - * PGPSecretKeyRing contains the masterkey and the new key as a subkey (certified by the - * masterkey). - * - * @param context - * @param algorithmChoice - * @param keySize - * @param passPhrase - * @param masterSecretKey - * @return - * @throws NoSuchAlgorithmException - * @throws PGPException - * @throws NoSuchProviderException - * @throws PgpGeneralException - * @throws InvalidAlgorithmParameterException - */ - public static PGPSecretKeyRing createKey(Context context, int algorithmChoice, int keySize, - String passPhrase, PGPSecretKey masterSecretKey) throws NoSuchAlgorithmException, - PGPException, NoSuchProviderException, PgpGeneralException, - InvalidAlgorithmParameterException { - - if (keySize < 512) { - throw new PgpGeneralException(context.getString(R.string.error_keySizeMinimum512bit)); - } - - if (passPhrase == null) { - passPhrase = ""; - } - - int algorithm = 0; - KeyPairGenerator keyGen = null; - - switch (algorithmChoice) { - case Id.choice.algorithm.dsa: { - keyGen = KeyPairGenerator.getInstance("DSA", BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - algorithm = PGPPublicKey.DSA; - break; - } - - case Id.choice.algorithm.elgamal: { - if (masterSecretKey == null) { - throw new PgpGeneralException( - context.getString(R.string.error_masterKeyMustNotBeElGamal)); - } - keyGen = KeyPairGenerator.getInstance("ElGamal", BOUNCY_CASTLE_PROVIDER_NAME); - BigInteger p = Primes.getBestPrime(keySize); - BigInteger g = new BigInteger("2"); - - ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); - - keyGen.initialize(elParams); - algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; - break; - } - - case Id.choice.algorithm.rsa: { - keyGen = KeyPairGenerator.getInstance("RSA", BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - - algorithm = PGPPublicKey.RSA_GENERAL; - break; - } - - default: { - throw new PgpGeneralException(context.getString(R.string.error_unknownAlgorithmChoice)); - } - } - - // build new key pair - PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - - // Build key encrypter and decrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - passPhrase.toCharArray()); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); - - PGPKeyRingGenerator ringGen = null; - PGPContentSignerBuilder certificationSignerBuilder = null; - if (masterSecretKey == null) { - certificationSignerBuilder = new JcaPGPContentSignerBuilder(keyPair.getPublicKey() - .getAlgorithm(), HashAlgorithmTags.SHA1); - - // build keyRing with only this one master key in it! - ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, keyPair, "", - sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); - } else { - PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); - PGPPrivateKey masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - certificationSignerBuilder = new JcaPGPContentSignerBuilder(masterKeyPair - .getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // build keyRing with master key and new key as subkey (certified by masterkey) - ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, - "", sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); - - ringGen.addSubKey(keyPair); - } - - PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); - - return secKeyRing; - } - - public static void changeSecretKeyPassphrase(Context context, PGPSecretKeyRing keyRing, - String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) - throws IOException, PGPException, PGPException, NoSuchProviderException { - - updateProgress(progress, R.string.progress_buildingKey, 0, 100); - if (oldPassPhrase == null) { - oldPassPhrase = ""; - } - if (newPassPhrase == null) { - newPassPhrase = ""; - } - - PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword( - keyRing, - new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()), - new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey() - .getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray())); - - updateProgress(progress, R.string.progress_savingKeyRing, 50, 100); - - ProviderHelper.saveKeyRing(context, newKeyRing); - - updateProgress(progress, R.string.progress_done, 100, 100); - - } - - public static void buildSecretKey(Context context, ArrayList userIds, - ArrayList keys, ArrayList keysUsages, long masterKeyId, - String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) - throws PgpGeneralException, NoSuchProviderException, PGPException, - NoSuchAlgorithmException, SignatureException, IOException { - - Log.d(Constants.TAG, "userIds: " + userIds.toString()); - - updateProgress(progress, R.string.progress_buildingKey, 0, 100); - - if (oldPassPhrase == null) { - oldPassPhrase = ""; - } - if (newPassPhrase == null) { - newPassPhrase = ""; - } - - updateProgress(progress, R.string.progress_preparingMasterKey, 10, 100); - - int usageId = keysUsages.get(0); - boolean canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); - boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); - - String mainUserId = userIds.get(0); - - PGPSecretKey masterKey = keys.get(0); - - // this removes all userIds and certifications previously attached to the masterPublicKey - PGPPublicKey tmpKey = masterKey.getPublicKey(); - PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), - tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); - - // already done by code above: - // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - // // Somehow, the PGPPublicKey already has an empty certification attached to it when the - // // keyRing is generated the first time, we remove that when it exists, before adding the - // new - // // ones - // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, - // ""); - // if (masterPublicKeyRmCert != null) { - // masterPublicKey = masterPublicKeyRmCert; - // } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(progress, R.string.progress_certifyingMasterKey, 20, 100); - - for (String userId : userIds) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - - // TODO: cross-certify the master key with every sub key (APG 1) - - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA; - if (canEncrypt) { - keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - hashedPacketsGen.setKeyFlags(true, keyFlags); - - hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - - // TODO: this doesn't work quite right yet (APG 1) - // if (keyEditor.getExpiryDate() != null) { - // GregorianCalendar creationDate = new GregorianCalendar(); - // creationDate.setTime(getCreationDate(masterKey)); - // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); - // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); - // if (numDays <= 0) { - // throw new GeneralException( - // context.getString(R.string.error_expiryMustComeAfterCreation)); - // } - // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); - // } - - updateProgress(progress, R.string.progress_buildingMasterKeyRing, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - newPassPhrase.toCharArray()); - - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - updateProgress(progress, R.string.progress_addingSubKeys, 40, 100); - - for (int i = 1; i < keys.size(); ++i) { - updateProgress(progress, 40 + 50 * (i - 1) / (keys.size() - 1), 100); - - PGPSecretKey subKey = keys.get(i); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - keyFlags = 0; - - usageId = keysUsages.get(i); - canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); - canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); - if (canSign) { - keyFlags |= KeyFlags.SIGN_DATA; - } - if (canEncrypt) { - keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - hashedPacketsGen.setKeyFlags(true, keyFlags); - - // TODO: this doesn't work quite right yet (APG 1) - // if (keyEditor.getExpiryDate() != null) { - // GregorianCalendar creationDate = new GregorianCalendar(); - // creationDate.setTime(getCreationDate(masterKey)); - // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); - // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); - // if (numDays <= 0) { - // throw new GeneralException( - // context.getString(R.string.error_expiryMustComeAfterCreation)); - // } - // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); - // } - - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - } - - PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); - PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); - - updateProgress(progress, R.string.progress_savingKeyRing, 90, 100); - - ProviderHelper.saveKeyRing(context, secretKeyRing); - ProviderHelper.saveKeyRing(context, publicKeyRing); - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - /** - * TODO: implement Id.return_value.updated as status when key already existed - * - * @param context - * @param keyring - * @return - */ - @SuppressWarnings("unchecked") - public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { - int status = Integer.MIN_VALUE; // out of bounds value (Id.return_value.*) - try { - if (keyring instanceof PGPSecretKeyRing) { - PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; - boolean save = true; - - for (PGPSecretKey testSecretKey : new IterableIterator( - secretKeyRing.getSecretKeys())) { - if (!testSecretKey.isMasterKey()) { - if (PgpHelper.isSecretKeyPrivateEmpty(testSecretKey)) { - // this is bad, something is very wrong... - save = false; - status = Id.return_value.bad; - } - } - } - - if (save) { - ProviderHelper.saveKeyRing(context, secretKeyRing); - // TODO: remove status returns, use exceptions! - status = Id.return_value.ok; - } - } else if (keyring instanceof PGPPublicKeyRing) { - PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; - ProviderHelper.saveKeyRing(context, publicKeyRing); - // TODO: remove status returns, use exceptions! - status = Id.return_value.ok; - } - } catch (IOException e) { - status = Id.return_value.error; - } - - return status; - } - - public static boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ArmoredOutputStream aos = new ArmoredOutputStream(bos); - try { - aos.write(keyring.getEncoded()); - aos.close(); - - String armouredKey = bos.toString("UTF-8"); - server.add(armouredKey); - - return true; - } catch (IOException e) { - return false; - } catch (AddKeyException e) { - // TODO: tell the user? - return false; - } finally { - try { - bos.close(); - } catch (IOException e) { - } - } - } - - public static Bundle importKeyRings(Context context, InputData data, - ProgressDialogUpdater progress) throws PgpGeneralException, FileNotFoundException, - PGPException, IOException { - Bundle returnData = new Bundle(); - - updateProgress(progress, R.string.progress_importingSecretKeys, 0, 100); - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new PgpGeneralException(context.getString(R.string.error_externalStorageNotReady)); - } - - PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream()); - - // need to have access to the bufferedInput, so we can reuse it for the possible - // PGPObject chunks after the first one, e.g. files with several consecutive ASCII - // armour blocks - BufferedInputStream bufferedInput = new BufferedInputStream(progressIn); - int newKeys = 0; - int oldKeys = 0; - int badKeys = 0; - try { - - // read all available blocks... (asc files can contain many blocks with BEGIN END) - while (bufferedInput.available() > 0) { - 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 keyring = (PGPKeyRing) obj; - - int status = Integer.MIN_VALUE; // out of bounds value - - status = storeKeyRingInCache(context, keyring); - - if (status == Id.return_value.error) { - throw new PgpGeneralException( - context.getString(R.string.error_savingKeys)); - } - - // update the counts to display to the user at the end - if (status == Id.return_value.updated) { - ++oldKeys; - } else if (status == Id.return_value.ok) { - ++newKeys; - } else if (status == Id.return_value.bad) { - ++badKeys; - } - - updateProgress(progress, - (int) (100 * progressIn.position() / data.getSize()), 100); - } else { - Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); - } - } - } - } catch (Exception e) { - Log.e(Constants.TAG, "Exception on parsing key file!", e); - } - - returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); - - updateProgress(progress, R.string.progress_done, 100, 100); - - return returnData; - } - - public static Bundle exportKeyRings(Context context, ArrayList keyRingMasterKeyIds, - int keyType, OutputStream outStream, ProgressDialogUpdater progress) - throws PgpGeneralException, FileNotFoundException, PGPException, IOException { - Bundle returnData = new Bundle(); - - if (keyRingMasterKeyIds.size() == 1) { - updateProgress(progress, R.string.progress_exportingKey, 0, 100); - } else { - updateProgress(progress, R.string.progress_exportingKeys, 0, 100); - } - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new PgpGeneralException(context.getString(R.string.error_externalStorageNotReady)); - } - - // export public keyrings... - ArmoredOutputStream outPub = new ArmoredOutputStream(outStream); - outPub.setHeader("Version", getFullVersion(context)); - - int numKeys = 0; - for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { - // double the needed time if exporting both public and secret parts - if (keyType == Id.type.secret_key) { - updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); - } else { - updateProgress(progress, i * 100 / keyRingMasterKeyIds.size(), 100); - } - - PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( - context, keyRingMasterKeyIds.get(i)); - - if (publicKeyRing != null) { - publicKeyRing.encode(outPub); - } - ++numKeys; - } - outPub.close(); - - // if we export secret keyrings, append all secret parts after the public parts - if (keyType == Id.type.secret_key) { - ArmoredOutputStream outSec = new ArmoredOutputStream(outStream); - outSec.setHeader("Version", getFullVersion(context)); - - for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { - updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); - - PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - context, keyRingMasterKeyIds.get(i)); - - if (secretKeyRing != null) { - secretKeyRing.encode(outSec); - } - } - outSec.close(); - } - - returnData.putInt(KeychainIntentService.RESULT_EXPORT, numKeys); - - updateProgress(progress, R.string.progress_done, 100, 100); - - return returnData; - } - - /** - * Encrypt and Sign data - * - * @param context - * @param progress - * @param data - * @param outStream - * @param useAsciiArmor - * @param compression - * @param encryptionKeyIds - * @param symmetricEncryptionAlgorithm - * @param encryptionPassphrase - * @param signatureKeyId - * @param signatureHashAlgorithm - * @param signatureForceV3 - * @param signaturePassphrase - * @throws IOException - * @throws PgpGeneralException - * @throws PGPException - * @throws NoSuchProviderException - * @throws NoSuchAlgorithmException - * @throws SignatureException - */ - public static void encryptAndSign(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean useAsciiArmor, int compression, - long[] encryptionKeyIds, String encryptionPassphrase, - int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, - boolean signatureForceV3, String signaturePassphrase) throws IOException, - PgpGeneralException, PGPException, NoSuchProviderException, NoSuchAlgorithmException, - SignatureException { - - if (encryptionKeyIds == null) { - encryptionKeyIds = new long[0]; - } - - ArmoredOutputStream armorOut = null; - OutputStream out = null; - OutputStream encryptOut = null; - if (useAsciiArmor) { - armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - out = armorOut; - } else { - out = outStream; - } - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (encryptionKeyIds.length == 0 && encryptionPassphrase == null) { - throw new PgpGeneralException( - context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); - } - - if (signatureKeyId != Id.key.none) { - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PgpHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassphrase == null) { - throw new PgpGeneralException( - context.getString(R.string.error_noSignaturePassPhrase)); - } - - updateProgress(progress, R.string.progress_extractingSignatureKey, 0, 100); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new PgpGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - } - updateProgress(progress, R.string.progress_preparingStreams, 5, 100); - - // encrypt and compress input file content - JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder( - symmetricEncryptionAlgorithm).setProvider(BOUNCY_CASTLE_PROVIDER_NAME) - .setWithIntegrityPacket(true); - - PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder); - - if (encryptionKeyIds.length == 0) { - // Symmetric encryption - Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption"); - - JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator( - encryptionPassphrase.toCharArray()); - cPk.addMethod(symmetricEncryptionGenerator); - } else { - // Asymmetric encryption - for (long id : encryptionKeyIds) { - PGPPublicKey key = PgpHelper.getEncryptPublicKey(context, id); - if (key != null) { - - JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator( - key); - cPk.addMethod(pubKeyEncryptionGenerator); - } - } - } - encryptOut = cPk.open(out, new byte[1 << 16]); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - if (signatureKeyId != Id.key.none) { - updateProgress(progress, R.string.progress_preparingSignature, 10, 100); - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (signatureForceV3) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); - - String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - } - - PGPCompressedDataGenerator compressGen = null; - BCPGOutputStream bcpgOut = null; - if (compression == Id.choice.compression.none) { - bcpgOut = new BCPGOutputStream(encryptOut); - } else { - compressGen = new PGPCompressedDataGenerator(compression); - bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut)); - } - if (signatureKeyId != Id.key.none) { - if (signatureForceV3) { - signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut); - } else { - signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); - } - } - - PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); - // file name not needed, so empty string - OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(), - new byte[1 << 16]); - updateProgress(progress, R.string.progress_encrypting, 20, 100); - - long done = 0; - int n = 0; - byte[] buffer = new byte[1 << 16]; - InputStream in = data.getInputStream(); - while ((n = in.read(buffer)) > 0) { - pOut.write(buffer, 0, n); - if (signatureKeyId != Id.key.none) { - if (signatureForceV3) { - signatureV3Generator.update(buffer, 0, n); - } else { - signatureGenerator.update(buffer, 0, n); - } - } - done += n; - if (data.getSize() != 0) { - updateProgress(progress, (int) (20 + (95 - 20) * done / data.getSize()), 100); - } - } - - literalGen.close(); - - if (signatureKeyId != Id.key.none) { - updateProgress(progress, R.string.progress_generatingSignature, 95, 100); - if (signatureForceV3) { - signatureV3Generator.generate().encode(pOut); - } else { - signatureGenerator.generate().encode(pOut); - } - } - if (compressGen != null) { - compressGen.close(); - } - encryptOut.close(); - if (useAsciiArmor) { - armorOut.close(); - } - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static void signText(Context context, ProgressDialogUpdater progress, InputData data, - OutputStream outStream, long signatureKeyId, String signaturePassphrase, - int signatureHashAlgorithm, boolean forceV3Signature) throws PgpGeneralException, - PGPException, IOException, NoSuchAlgorithmException, SignatureException { - - ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (signatureKeyId == 0) { - armorOut.close(); - throw new PgpGeneralException(context.getString(R.string.error_noSignatureKey)); - } - - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PgpHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - armorOut.close(); - throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassphrase == null) { - armorOut.close(); - throw new PgpGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); - } - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - armorOut.close(); - throw new PgpGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - updateProgress(progress, R.string.progress_preparingStreams, 0, 100); - - updateProgress(progress, R.string.progress_preparingSignature, 30, 100); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey - .getPublicKey().getAlgorithm(), signatureHashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (forceV3Signature) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - - updateProgress(progress, R.string.progress_signing, 40, 100); - - armorOut.beginClearText(signatureHashAlgorithm); - - InputStream inStream = data.getInputStream(); - final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); - - final byte[] newline = "\r\n".getBytes("UTF-8"); - - if (forceV3Signature) { - processLine(reader.readLine(), armorOut, signatureV3Generator); - } else { - processLine(reader.readLine(), armorOut, signatureGenerator); - } - - while (true) { - final String line = reader.readLine(); - - if (line == null) { - armorOut.write(newline); - break; - } - - armorOut.write(newline); - if (forceV3Signature) { - signatureV3Generator.update(newline); - processLine(line, armorOut, signatureV3Generator); - } else { - signatureGenerator.update(newline); - processLine(line, armorOut, signatureGenerator); - } - } - - armorOut.endClearText(); - - BCPGOutputStream bOut = new BCPGOutputStream(armorOut); - if (forceV3Signature) { - signatureV3Generator.generate().encode(bOut); - } else { - signatureGenerator.generate().encode(bOut); - } - armorOut.close(); - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static void generateSignature(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean armored, boolean binary, - long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, - boolean forceV3Signature) throws PgpGeneralException, PGPException, IOException, - NoSuchAlgorithmException, SignatureException { - - OutputStream out = null; - - // Ascii Armor (Base64) - ArmoredOutputStream armorOut = null; - if (armored) { - armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - out = armorOut; - } else { - out = outStream; - } - - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (signatureKeyId == 0) { - throw new PgpGeneralException(context.getString(R.string.error_noSignatureKey)); - } - - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PgpHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassPhrase == null) { - throw new PgpGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new PgpGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - updateProgress(progress, R.string.progress_preparingStreams, 0, 100); - - updateProgress(progress, R.string.progress_preparingSignature, 30, 100); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; - if (binary) { - type = PGPSignature.BINARY_DOCUMENT; - } - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey - .getPublicKey().getAlgorithm(), hashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (forceV3Signature) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(type, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(type, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - - updateProgress(progress, R.string.progress_signing, 40, 100); - - InputStream inStream = data.getInputStream(); - if (binary) { - byte[] buffer = new byte[1 << 16]; - int n = 0; - while ((n = inStream.read(buffer)) > 0) { - if (forceV3Signature) { - signatureV3Generator.update(buffer, 0, n); - } else { - signatureGenerator.update(buffer, 0, n); - } - } - } else { - final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); - final byte[] newline = "\r\n".getBytes("UTF-8"); - - while (true) { - final String line = reader.readLine(); - - if (line == null) { - break; - } - - if (forceV3Signature) { - processLine(line, null, signatureV3Generator); - signatureV3Generator.update(newline); - } else { - processLine(line, null, signatureGenerator); - signatureGenerator.update(newline); - } - } - } - - BCPGOutputStream bOut = new BCPGOutputStream(out); - if (forceV3Signature) { - signatureV3Generator.generate().encode(bOut); - } else { - signatureGenerator.generate().encode(bOut); - } - out.close(); - outStream.close(); - - if (progress != null) - progress.setProgress(R.string.progress_done, 100, 100); - } - - public static PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId, - String passphrase) throws PgpGeneralException, NoSuchAlgorithmException, - NoSuchProviderException, PGPException, SignatureException { - if (passphrase == null || passphrase.length() <= 0) { - throw new PgpGeneralException("Unable to obtain passphrase"); - } else { - PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); - - PGPSecretKey signingKey = PgpHelper.getCertificationKey(context, masterKeyId); - if (signingKey == null) { - throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new PgpGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - - // TODO: SHA256 fixed? - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( - contentSignerBuilder); - - signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - - PGPSignatureSubpacketVector packetVector = spGen.generate(); - signatureGenerator.setHashedSubpackets(packetVector); - - PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), - signatureGenerator.generate()); - pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey); - - return pubring; - } - } - - public static long getDecryptionKeyId(Context context, InputStream inputStream) - throws PgpGeneralException, NoAsymmetricEncryptionException, IOException { - InputStream in = PGPUtil.getDecoderStream(inputStream); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - - // the first object might be a PGP marker packet. - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - throw new PgpGeneralException(context.getString(R.string.error_invalidData)); - } - - // TODO: currently we always only look at the first known key - // find the secret key - PGPSecretKey secretKey = null; - Iterator it = enc.getEncryptedDataObjects(); - boolean gotAsymmetricEncryption = false; - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPublicKeyEncryptedData) { - gotAsymmetricEncryption = true; - PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; - secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, pbe.getKeyID()); - if (secretKey != null) { - break; - } - } - } - - if (!gotAsymmetricEncryption) { - throw new NoAsymmetricEncryptionException(); - } - - if (secretKey == null) { - return Id.key.none; - } - - return secretKey.getKeyID(); - } - - public static boolean hasSymmetricEncryption(Context context, InputStream inputStream) - throws PgpGeneralException, IOException { - InputStream in = PGPUtil.getDecoderStream(inputStream); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - - // the first object might be a PGP marker packet. - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - throw new PgpGeneralException(context.getString(R.string.error_invalidData)); - } - - Iterator it = enc.getEncryptedDataObjects(); - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPBEEncryptedData) { - return true; - } - } - - return false; - } - - public static Bundle decryptAndVerify(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, String passphrase, boolean assumeSymmetric) - throws IOException, PgpGeneralException, PGPException, SignatureException { - if (passphrase == null) { - passphrase = ""; - } - - Bundle returnData = new Bundle(); - InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - long signatureKeyId = 0; - - int currentProgress = 0; - if (progress != null) - progress.setProgress(R.string.progress_readingData, currentProgress, 100); - - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - throw new PgpGeneralException(context.getString(R.string.error_invalidData)); - } - - InputStream clear = null; - PGPEncryptedData encryptedData = null; - - currentProgress += 5; - - // TODO: currently we always only look at the first known key or symmetric encryption, - // there might be more... - if (assumeSymmetric) { - PGPPBEEncryptedData pbe = null; - Iterator it = enc.getEncryptedDataObjects(); - // find secret key - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPBEEncryptedData) { - pbe = (PGPPBEEncryptedData) obj; - break; - } - } - - if (pbe == null) { - throw new PgpGeneralException( - context.getString(R.string.error_noSymmetricEncryptionPacket)); - } - - updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); - - PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(); - PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( - digestCalcProvider).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.toCharArray()); - - clear = pbe.getDataStream(decryptorFactory); - - encryptedData = pbe; - currentProgress += 5; - } else { - if (progress != null) - progress.setProgress(R.string.progress_findingKey, currentProgress, 100); - PGPPublicKeyEncryptedData pbe = null; - PGPSecretKey secretKey = null; - Iterator it = enc.getEncryptedDataObjects(); - // find secret key - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPublicKeyEncryptedData) { - PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; - secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); - if (secretKey != null) { - pbe = encData; - break; - } - } - } - - if (secretKey == null) { - throw new PgpGeneralException(context.getString(R.string.error_noSecretKeyFound)); - } - - currentProgress += 5; - updateProgress(progress, R.string.progress_extractingKey, currentProgress, 100); - PGPPrivateKey privateKey = null; - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - privateKey = secretKey.extractPrivateKey(keyDecryptor); - } catch (PGPException e) { - throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); - } - if (privateKey == null) { - throw new PgpGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - currentProgress += 5; - updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); - - PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey); - - clear = pbe.getDataStream(decryptorFactory); - - encryptedData = pbe; - currentProgress += 5; - } - - PGPObjectFactory plainFact = new PGPObjectFactory(clear); - Object dataChunk = plainFact.nextObject(); - PGPOnePassSignature signature = null; - PGPPublicKey signatureKey = null; - int signatureIndex = -1; - - if (dataChunk instanceof PGPCompressedData) { - if (progress != null) - progress.setProgress(R.string.progress_decompressingData, currentProgress, 100); - PGPObjectFactory fact = new PGPObjectFactory( - ((PGPCompressedData) dataChunk).getDataStream()); - dataChunk = fact.nextObject(); - plainFact = fact; - currentProgress += 10; - } - - if (dataChunk instanceof PGPOnePassSignatureList) { - if (progress != null) - progress.setProgress(R.string.progress_processingSignature, currentProgress, 100); - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); - PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; - for (int i = 0; i < sigList.size(); ++i) { - signature = sigList.get(i); - signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); - if (signatureKeyId == 0) { - signatureKeyId = signature.getKeyID(); - } - if (signatureKey == null) { - signature = null; - } else { - signatureIndex = i; - signatureKeyId = signature.getKeyID(); - String userId = null; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId( - context, signatureKeyId); - if (signKeyRing != null) { - userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signKeyRing)); - } - returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); - break; - } - } - - returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - - if (signature != null) { - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - signature.init(contentVerifierBuilderProvider, signatureKey); - } else { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); - } - - dataChunk = plainFact.nextObject(); - currentProgress += 10; - } - - if (dataChunk instanceof PGPSignatureList) { - dataChunk = plainFact.nextObject(); - } - - if (dataChunk instanceof PGPLiteralData) { - if (progress != null) - progress.setProgress(R.string.progress_decrypting, currentProgress, 100); - PGPLiteralData literalData = (PGPLiteralData) dataChunk; - OutputStream out = outStream; - - byte[] buffer = new byte[1 << 16]; - InputStream dataIn = literalData.getInputStream(); - - int startProgress = currentProgress; - int endProgress = 100; - if (signature != null) { - endProgress = 90; - } else if (encryptedData.isIntegrityProtected()) { - endProgress = 95; - } - int n = 0; - int done = 0; - long startPos = data.getStreamPosition(); - while ((n = dataIn.read(buffer)) > 0) { - out.write(buffer, 0, n); - done += n; - if (signature != null) { - try { - signature.update(buffer, 0, n); - } catch (SignatureException e) { - returnData - .putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); - signature = null; - } - } - // unknown size, but try to at least have a moving, slowing down progress bar - currentProgress = startProgress + (endProgress - startProgress) * done - / (done + 100000); - if (data.getSize() - startPos == 0) { - currentProgress = endProgress; - } else { - currentProgress = (int) (startProgress + (endProgress - startProgress) - * (data.getStreamPosition() - startPos) / (data.getSize() - startPos)); - } - updateProgress(progress, currentProgress, 100); - } - - if (signature != null) { - if (progress != null) - progress.setProgress(R.string.progress_verifyingSignature, 90, 100); - PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); - PGPSignature messageSignature = signatureList.get(signatureIndex); - if (signature.verify(messageSignature)) { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, true); - } else { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); - } - } - } - - // TODO: add integrity somewhere - if (encryptedData.isIntegrityProtected()) { - if (progress != null) - progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100); - if (encryptedData.verify()) { - // passed - } else { - // failed - } - } else { - // no integrity check - } - - updateProgress(progress, R.string.progress_done, 100, 100); - return returnData; - } - - public static Bundle verifyText(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean lookupUnknownKey) throws IOException, - PgpGeneralException, PGPException, SignatureException { - Bundle returnData = new Bundle(); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); - - updateProgress(progress, R.string.progress_done, 0, 100); - - // mostly taken from ClearSignedFileProcessor - ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); - int lookAhead = readInputLine(lineOut, aIn); - byte[] lineSep = getLineSeparator(); - - byte[] line = lineOut.toByteArray(); - out.write(line, 0, getLengthWithoutSeparator(line)); - out.write(lineSep); - - while (lookAhead != -1 && aIn.isClearText()) { - lookAhead = readInputLine(lineOut, lookAhead, aIn); - line = lineOut.toByteArray(); - out.write(line, 0, getLengthWithoutSeparator(line)); - out.write(lineSep); - } - - out.close(); - - byte[] clearText = out.toByteArray(); - outStream.write(clearText); - - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); - - updateProgress(progress, R.string.progress_processingSignature, 60, 100); - PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); - - PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); - if (sigList == null) { - throw new PgpGeneralException(context.getString(R.string.error_corruptData)); - } - PGPSignature signature = null; - long signatureKeyId = 0; - PGPPublicKey signatureKey = null; - for (int i = 0; i < sigList.size(); ++i) { - signature = sigList.get(i); - signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); - if (signatureKeyId == 0) { - signatureKeyId = signature.getKeyID(); - } - // if key is not known and we want to lookup unknown ones... - if (signatureKey == null && lookupUnknownKey) { - - returnData = new Bundle(); - returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true); - - // return directly now, decrypt will be done again after importing unknown key - return returnData; - } - - if (signatureKey == null) { - signature = null; - } else { - signatureKeyId = signature.getKeyID(); - String userId = null; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, - signatureKeyId); - if (signKeyRing != null) { - userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signKeyRing)); - } - returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); - break; - } - } - - returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - - if (signature == null) { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); - if (progress != null) - progress.setProgress(R.string.progress_done, 100, 100); - return returnData; - } - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - signature.init(contentVerifierBuilderProvider, signatureKey); - - InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); - - lookAhead = readInputLine(lineOut, sigIn); - - processLine(signature, lineOut.toByteArray()); - - if (lookAhead != -1) { - do { - lookAhead = readInputLine(lineOut, lookAhead, sigIn); - - signature.update((byte) '\r'); - signature.update((byte) '\n'); - - processLine(signature, lineOut.toByteArray()); - } while (lookAhead != -1); - } - - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); - - updateProgress(progress, R.string.progress_done, 100, 100); - return returnData; - } - - public static int getStreamContent(Context context, InputStream inStream) throws IOException { - InputStream in = PGPUtil.getDecoderStream(inStream); - PGPObjectFactory pgpF = new PGPObjectFactory(in); - Object object = pgpF.nextObject(); - while (object != null) { - if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) { - return Id.content.keys; - } else if (object instanceof PGPEncryptedDataList) { - return Id.content.encrypted_data; - } - object = pgpF.nextObject(); - } - - return Id.content.unknown; - } - - private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, - final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException { - - if (pLine == null) { - return; - } - - final char[] chars = pLine.toCharArray(); - int len = chars.length; - - while (len > 0) { - if (!Character.isWhitespace(chars[len - 1])) { - break; - } - len--; - } - - final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); - - if (pArmoredOutput != null) { - pArmoredOutput.write(data); - } - pSignatureGenerator.update(data); - } - - private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, - final PGPV3SignatureGenerator pSignatureGenerator) throws IOException, - SignatureException { - - if (pLine == null) { - return; - } - - final char[] chars = pLine.toCharArray(); - int len = chars.length; - - while (len > 0) { - if (!Character.isWhitespace(chars[len - 1])) { - break; - } - len--; - } - - final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); - - if (pArmoredOutput != null) { - pArmoredOutput.write(data); - } - pSignatureGenerator.update(data); - } - - // taken from ClearSignedFileProcessor in BC - private static void processLine(PGPSignature sig, byte[] line) throws SignatureException, - IOException { - int length = getLengthWithoutWhiteSpace(line); - if (length > 0) { - sig.update(line, 0, length); - } - } - - private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) - throws IOException { - bOut.reset(); - - int lookAhead = -1; - int ch; - - while ((ch = fIn.read()) >= 0) { - bOut.write(ch); - if (ch == '\r' || ch == '\n') { - lookAhead = readPassedEOL(bOut, ch, fIn); - break; - } - } - - return lookAhead; - } - - private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) - throws IOException { - bOut.reset(); - - int ch = lookAhead; - - do { - bOut.write(ch); - if (ch == '\r' || ch == '\n') { - lookAhead = readPassedEOL(bOut, ch, fIn); - break; - } - } while ((ch = fIn.read()) >= 0); - - if (ch < 0) { - lookAhead = -1; - } - - return lookAhead; - } - - private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) - throws IOException { - int lookAhead = fIn.read(); - - if (lastCh == '\r' && lookAhead == '\n') { - bOut.write(lookAhead); - lookAhead = fIn.read(); - } - - return lookAhead; - } - - private static int getLengthWithoutSeparator(byte[] line) { - int end = line.length - 1; - - while (end >= 0 && isLineEnding(line[end])) { - end--; - } - - return end + 1; - } - - private static boolean isLineEnding(byte b) { - return b == '\r' || b == '\n'; - } - - private static int getLengthWithoutWhiteSpace(byte[] line) { - int end = line.length - 1; - - while (end >= 0 && isWhiteSpace(line[end])) { - end--; - } - - return end + 1; - } - - private static boolean isWhiteSpace(byte b) { - return b == '\r' || b == '\n' || b == '\t' || b == ' '; - } - - private static byte[] getLineSeparator() { - String nl = System.getProperty("line.separator"); - byte[] nlBytes = new byte[nl.length()]; - - for (int i = 0; i != nlBytes.length; i++) { - nlBytes[i] = (byte) nl.charAt(i); - } - - return nlBytes; - } - - public static boolean isReleaseVersion(Context context) { - try { - PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); - if (pi.versionCode % 100 == 99) { - return true; - } else { - return false; - } - } catch (NameNotFoundException e) { - // impossible! - return false; - } - } - - public static String getVersion(Context context) { - String version = null; - try { - PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); - version = pi.versionName; - return version; - } catch (NameNotFoundException e) { - Log.e(Constants.TAG, "Version could not be retrieved!", e); - return "0.0.0"; - } - } - - public static String getFullVersion(Context context) { - return "OpenPGP Keychain v" + getVersion(context); - } - - /** - * Generate a random filename - * - * @param length - * @return - */ - public static String generateRandomFilename(int length) { - SecureRandom random = new SecureRandom(); - - byte bytes[] = new byte[length]; - random.nextBytes(bytes); - String result = ""; - for (int i = 0; i < length; ++i) { - int v = (bytes[i] + 256) % 64; - if (v < 10) { - result += (char) ('0' + v); - } else if (v < 36) { - result += (char) ('A' + v - 10); - } else if (v < 62) { - result += (char) ('a' + v - 36); - } else if (v == 62) { - result += '_'; - } else if (v == 63) { - result += '.'; - } - } - return result; - } - - /** - * Go once through stream to get length of stream. The length is later used to display progress - * when encrypting/decrypting - * - * @param in - * @return - * @throws IOException - */ - public static long getLengthOfStream(InputStream in) throws IOException { - long size = 0; - long n = 0; - byte dummy[] = new byte[0x10000]; - while ((n = in.read(dummy)) > 0) { - size += n; - } - return size; - } - - /** - * Deletes file securely by overwriting it with random data before deleting it. - * - * TODO: Does this really help on flash storage? - * - * @param context - * @param progress - * @param file - * @throws FileNotFoundException - * @throws IOException - */ - public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file) - throws FileNotFoundException, IOException { - long length = file.length(); - SecureRandom random = new SecureRandom(); - RandomAccessFile raf = new RandomAccessFile(file, "rws"); - raf.seek(0); - raf.getFilePointer(); - byte[] data = new byte[1 << 16]; - int pos = 0; - String msg = context.getString(R.string.progress_deletingSecurely, file.getName()); - while (pos < length) { - if (progress != null) - progress.setProgress(msg, (int) (100 * pos / length), 100); - random.nextBytes(data); - raf.write(data); - pos += data.length; - } - raf.close(); - file.delete(); - } -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java deleted file mode 100644 index 34815a53d..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java +++ /dev/null @@ -1,307 +0,0 @@ -package org.sufficientlysecure.keychain.helper; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.SignatureException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.text.DateFormat; -import java.util.Date; -import java.util.Iterator; -import java.util.Vector; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; - -import org.spongycastle.asn1.DERObjectIdentifier; -import org.spongycastle.asn1.x509.AuthorityKeyIdentifier; -import org.spongycastle.asn1.x509.BasicConstraints; -import org.spongycastle.asn1.x509.GeneralName; -import org.spongycastle.asn1.x509.GeneralNames; -import org.spongycastle.asn1.x509.SubjectKeyIdentifier; -import org.spongycastle.asn1.x509.X509Extensions; -import org.spongycastle.asn1.x509.X509Name; -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.x509.X509V3CertificateGenerator; -import org.spongycastle.x509.extension.AuthorityKeyIdentifierStructure; -import org.spongycastle.x509.extension.SubjectKeyIdentifierStructure; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.util.Log; - -public class PgpToX509 { - public final static String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge"; - public final static String DN_COMMON_PART_OU = "OpenPGP Keychain cert"; - - /** - * Creates a self-signed certificate from a public and private key. The (critical) key-usage - * extension is set up with: digital signature, non-repudiation, key-encipherment, key-agreement - * and certificate-signing. The (non-critical) Netscape extension is set up with: SSL client and - * S/MIME. A URI subjectAltName may also be set up. - * - * @param pubKey - * public key - * @param privKey - * private key - * @param subject - * subject (and issuer) DN for this certificate, RFC 2253 format preferred. - * @param startDate - * date from which the certificate will be valid (defaults to current date and time - * if null) - * @param endDate - * date until which the certificate will be valid (defaults to current date and time - * if null) * - * @param subjAltNameURI - * URI to be placed in subjectAltName - * @return self-signed certificate - * @throws InvalidKeyException - * @throws SignatureException - * @throws NoSuchAlgorithmException - * @throws IllegalStateException - * @throws NoSuchProviderException - * @throws CertificateException - * @throws Exception - * - * @author Bruno Harbulot - */ - public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey, - X509Name subject, Date startDate, Date endDate, String subjAltNameURI) - throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, - SignatureException, CertificateException, NoSuchProviderException { - - X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator(); - - certGenerator.reset(); - /* - * Sets up the subject distinguished name. Since it's a self-signed certificate, issuer and - * subject are the same. - */ - certGenerator.setIssuerDN(subject); - certGenerator.setSubjectDN(subject); - - /* - * Sets up the validity dates. - */ - if (startDate == null) { - startDate = new Date(System.currentTimeMillis()); - } - certGenerator.setNotBefore(startDate); - if (endDate == null) { - endDate = new Date(startDate.getTime() + (365L * 24L * 60L * 60L * 1000L)); - Log.d(Constants.TAG, "end date is=" + DateFormat.getDateInstance().format(endDate)); - } - - certGenerator.setNotAfter(endDate); - - /* - * The serial-number of this certificate is 1. It makes sense because it's self-signed. - */ - certGenerator.setSerialNumber(BigInteger.ONE); - /* - * Sets the public-key to embed in this certificate. - */ - certGenerator.setPublicKey(pubKey); - /* - * Sets the signature algorithm. - */ - String pubKeyAlgorithm = pubKey.getAlgorithm(); - if (pubKeyAlgorithm.equals("DSA")) { - certGenerator.setSignatureAlgorithm("SHA1WithDSA"); - } else if (pubKeyAlgorithm.equals("RSA")) { - certGenerator.setSignatureAlgorithm("SHA1WithRSAEncryption"); - } else { - RuntimeException re = new RuntimeException("Algorithm not recognised: " - + pubKeyAlgorithm); - Log.e(Constants.TAG, re.getMessage(), re); - throw re; - } - - /* - * Adds the Basic Constraint (CA: true) extension. - */ - certGenerator.addExtension(X509Extensions.BasicConstraints, true, - new BasicConstraints(true)); - - /* - * Adds the subject key identifier extension. - */ - SubjectKeyIdentifier subjectKeyIdentifier = new SubjectKeyIdentifierStructure(pubKey); - certGenerator - .addExtension(X509Extensions.SubjectKeyIdentifier, false, subjectKeyIdentifier); - - /* - * Adds the authority key identifier extension. - */ - AuthorityKeyIdentifier authorityKeyIdentifier = new AuthorityKeyIdentifierStructure(pubKey); - certGenerator.addExtension(X509Extensions.AuthorityKeyIdentifier, false, - authorityKeyIdentifier); - - /* - * Adds the subject alternative-name extension. - */ - if (subjAltNameURI != null) { - GeneralNames subjectAltNames = new GeneralNames(new GeneralName( - GeneralName.uniformResourceIdentifier, subjAltNameURI)); - certGenerator.addExtension(X509Extensions.SubjectAlternativeName, false, - subjectAltNames); - } - - /* - * Creates and sign this certificate with the private key corresponding to the public key of - * the certificate (hence the name "self-signed certificate"). - */ - X509Certificate cert = certGenerator.generate(privKey); - - /* - * Checks that this certificate has indeed been correctly signed. - */ - cert.verify(pubKey); - - return cert; - } - - /** - * Creates a self-signed certificate from a PGP Secret Key. - * - * @param pgpSecKey - * PGP Secret Key (from which one can extract the public and private keys and other - * attributes). - * @param pgpPrivKey - * PGP Private Key corresponding to the Secret Key (password callbacks should be done - * before calling this method) - * @param subjAltNameURI - * optional URI to embed in the subject alternative-name - * @return self-signed certificate - * @throws PGPException - * @throws NoSuchProviderException - * @throws InvalidKeyException - * @throws NoSuchAlgorithmException - * @throws SignatureException - * @throws CertificateException - * - * @author Bruno Harbulot - */ - public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey, - PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException, - NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException, - SignatureException, CertificateException { - // get public key from secret key - PGPPublicKey pgpPubKey = pgpSecKey.getPublicKey(); - - // LOGGER.info("Key ID: " + Long.toHexString(pgpPubKey.getKeyID() & 0xffffffffL)); - - /* - * The X.509 Name to be the subject DN is prepared. The CN is extracted from the Secret Key - * user ID. - */ - Vector x509NameOids = new Vector(); - Vector x509NameValues = new Vector(); - - x509NameOids.add(X509Name.O); - x509NameValues.add(DN_COMMON_PART_O); - - x509NameOids.add(X509Name.OU); - x509NameValues.add(DN_COMMON_PART_OU); - - for (@SuppressWarnings("unchecked") - Iterator it = (Iterator) pgpSecKey.getUserIDs(); it.hasNext();) { - Object attrib = it.next(); - x509NameOids.add(X509Name.CN); - x509NameValues.add("CryptoCall"); - // x509NameValues.add(attrib.toString()); - } - - /* - * Currently unused. - */ - Log.d(Constants.TAG, "User attributes: "); - for (@SuppressWarnings("unchecked") - Iterator it = (Iterator) pgpSecKey.getUserAttributes(); it.hasNext();) { - Object attrib = it.next(); - Log.d(Constants.TAG, " - " + attrib + " -- " + attrib.getClass()); - } - - X509Name x509name = new X509Name(x509NameOids, x509NameValues); - - Log.d(Constants.TAG, "Subject DN: " + x509name); - - /* - * To check the signature from the certificate on the recipient side, the creation time - * needs to be embedded in the certificate. It seems natural to make this creation time be - * the "not-before" date of the X.509 certificate. Unlimited PGP keys have a validity of 0 - * second. In this case, the "not-after" date will be the same as the not-before date. This - * is something that needs to be checked by the service receiving this certificate. - */ - Date creationTime = pgpPubKey.getCreationTime(); - Log.d(Constants.TAG, - "pgp pub key creation time=" + DateFormat.getDateInstance().format(creationTime)); - Log.d(Constants.TAG, "pgp valid seconds=" + pgpPubKey.getValidSeconds()); - Date validTo = null; - if (pgpPubKey.getValidSeconds() > 0) { - validTo = new Date(creationTime.getTime() + 1000L * pgpPubKey.getValidSeconds()); - } - - X509Certificate selfSignedCert = createSelfSignedCert( - pgpPubKey.getKey(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME), pgpPrivKey.getKey(), - x509name, creationTime, validTo, subjAltNameURI); - - return selfSignedCert; - } - - /** - * This is a password callback handler that will fill in a password automatically. Useful to - * configure passwords in advance, but should be used with caution depending on how much you - * allow passwords to be stored within your application. - * - * @author Bruno Harbulot. - * - */ - public final static class PredefinedPasswordCallbackHandler implements CallbackHandler { - - private char[] password; - private String prompt; - - public PredefinedPasswordCallbackHandler(String password) { - this(password == null ? null : password.toCharArray(), null); - } - - public PredefinedPasswordCallbackHandler(char[] password) { - this(password, null); - } - - public PredefinedPasswordCallbackHandler(String password, String prompt) { - this(password == null ? null : password.toCharArray(), prompt); - } - - public PredefinedPasswordCallbackHandler(char[] password, String prompt) { - this.password = password; - this.prompt = prompt; - } - - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (Callback callback : callbacks) { - if (callback instanceof PasswordCallback) { - PasswordCallback pwCallback = (PasswordCallback) callback; - if ((this.prompt == null) || (this.prompt.equals(pwCallback.getPrompt()))) { - pwCallback.setPassword(this.password); - } - } else { - throw new UnsupportedCallbackException(callback, "Unrecognised callback."); - } - } - } - - protected final Object clone() throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - } -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java new file mode 100644 index 000000000..e406a142e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2012-2013 Dominik Schürmann + * + * 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 . + */ + +package org.sufficientlysecure.keychain.pgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + + +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 + * + * @param keysBytes + * @return + */ + public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { + PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); + ArrayList keys = new ArrayList(); + + @SuppressWarnings("unchecked") + Iterator itr = keyRing.getSecretKeys(); + while (itr.hasNext()) { + keys.add(itr.next()); + } + + return keys; + } + + /** + * Convert from byte[] to PGPSecretKey + * + * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle + * + * @param keysBytes + * @return + */ + public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { + PGPSecretKey key = BytesToPGPSecretKeyList(keyBytes).get(0); + + return key; + } + + /** + * Convert from ArrayList to byte[] + * + * @param keys + * @return + */ + public static byte[] PGPSecretKeyArrayListToBytes(ArrayList keys) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (PGPSecretKey key : keys) { + try { + key.encode(os); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); + } + } + + return os.toByteArray(); + } + + /** + * Convert from PGPSecretKey to byte[] + * + * @param keysBytes + * @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 keysBytes + * @return + */ + public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) { + try { + return keyRing.getEncoded(); + } catch (IOException e) { + Log.e(Constants.TAG, "Encoding failed", e); + + return null; + } + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpHelper.java new file mode 100644 index 000000000..b1e332441 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.pgp; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.Vector; + +import org.spongycastle.bcpg.sig.KeyFlags; +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.PGPSignatureSubpacketVector; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.Context; + +public class PgpHelper { + + public static Date getCreationDate(PGPPublicKey key) { + return key.getCreationTime(); + } + + public static Date getCreationDate(PGPSecretKey key) { + return key.getPublicKey().getCreationTime(); + } + + @SuppressWarnings("unchecked") + public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) { + if (keyRing == null) { + return null; + } + for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + if (key.isMasterKey()) { + return key; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) { + if (keyRing == null) { + return null; + } + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (key.isMasterKey()) { + return key; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) { + long cnt = 0; + if (keyRing == null) { + return null; + } + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (cnt == num) { + return key; + } + cnt++; + } + + return null; + } + + @SuppressWarnings("unchecked") + public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) { + Vector encryptKeys = new Vector(); + + for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + if (isEncryptionKey(key)) { + encryptKeys.add(key); + } + } + + return encryptKeys; + } + + @SuppressWarnings("unchecked") + public static Vector getSigningKeys(PGPSecretKeyRing keyRing) { + Vector signingKeys = new Vector(); + + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (isSigningKey(key)) { + signingKeys.add(key); + } + } + + return signingKeys; + } + + @SuppressWarnings("unchecked") + public static Vector getCertificationKeys(PGPSecretKeyRing keyRing) { + Vector signingKeys = new Vector(); + + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (isCertificationKey(key)) { + signingKeys.add(key); + } + } + + return signingKeys; + } + + public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) { + Vector usableKeys = new Vector(); + Vector encryptKeys = getEncryptKeys(keyRing); + PGPPublicKey masterKey = null; + for (int i = 0; i < encryptKeys.size(); ++i) { + PGPPublicKey key = encryptKeys.get(i); + if (!isExpired(key)) { + if (key.isMasterKey()) { + masterKey = key; + } else { + usableKeys.add(key); + } + } + } + if (masterKey != null) { + usableKeys.add(masterKey); + } + return usableKeys; + } + + 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; + } + + public static boolean isExpired(PGPSecretKey key) { + return isExpired(key.getPublicKey()); + } + + public static Vector getUsableCertificationKeys(PGPSecretKeyRing keyRing) { + Vector usableKeys = new Vector(); + Vector 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; + } + + public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) { + Vector usableKeys = new Vector(); + Vector 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 Date getExpiryDate(PGPPublicKey key) { + Date creationDate = getCreationDate(key); + if (key.getValidDays() == 0) { + // no expiry + return null; + } + Calendar calendar = GregorianCalendar.getInstance(); + calendar.setTime(creationDate); + calendar.add(Calendar.DATE, key.getValidDays()); + Date expiryDate = calendar.getTime(); + + return expiryDate; + } + + public static Date getExpiryDate(PGPSecretKey key) { + return getExpiryDate(key.getPublicKey()); + } + + public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, + masterKeyId); + if (keyRing == null) { + Log.e(Constants.TAG, "keyRing is null!"); + return null; + } + Vector encryptKeys = getUsableEncryptKeys(keyRing); + if (encryptKeys.size() == 0) { + Log.e(Constants.TAG, "encryptKeys is null!"); + return null; + } + return encryptKeys.get(0); + } + + public static PGPSecretKey getCertificationKey(Context context, long masterKeyId) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, + masterKeyId); + if (keyRing == null) { + return null; + } + Vector signingKeys = getUsableCertificationKeys(keyRing); + if (signingKeys.size() == 0) { + return null; + } + return signingKeys.get(0); + } + + public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, + masterKeyId); + if (keyRing == null) { + return null; + } + Vector signingKeys = getUsableSigningKeys(keyRing); + if (signingKeys.size() == 0) { + return null; + } + return signingKeys.get(0); + } + + @SuppressWarnings("unchecked") + public static String getMainUserId(PGPPublicKey key) { + for (String userId : new IterableIterator(key.getUserIDs())) { + return userId; + } + return null; + } + + @SuppressWarnings("unchecked") + public static String getMainUserId(PGPSecretKey key) { + for (String userId : new IterableIterator(key.getUserIDs())) { + return userId; + } + return null; + } + + public static String getMainUserIdSafe(Context context, PGPPublicKey key) { + String userId = getMainUserId(key); + if (userId == null || userId.equals("")) { + userId = context.getString(R.string.unknownUserId); + } + return userId; + } + + public static String getMainUserIdSafe(Context context, PGPSecretKey key) { + String userId = getMainUserId(key); + if (userId == null || userId.equals("")) { + userId = context.getString(R.string.unknownUserId); + } + return userId; + } + + @SuppressWarnings("unchecked") + public static boolean isEncryptionKey(PGPPublicKey key) { + if (!key.isEncryptionKey()) { + return false; + } + + if (key.getVersion() <= 3) { + // this must be true now + return key.isEncryptionKey(); + } + + // special cases + if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) { + return true; + } + + if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null + && (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null + && (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) { + return true; + } + } + return false; + } + + public static boolean isEncryptionKey(PGPSecretKey key) { + return isEncryptionKey(key.getPublicKey()); + } + + @SuppressWarnings("unchecked") + public static boolean isSigningKey(PGPPublicKey key) { + if (key.getVersion() <= 3) { + return true; + } + + // special case + if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) { + return true; + } + } + + return false; + } + + public static boolean isSigningKey(PGPSecretKey key) { + return isSigningKey(key.getPublicKey()); + } + + @SuppressWarnings("unchecked") + public static boolean isCertificationKey(PGPPublicKey key) { + if (key.getVersion() <= 3) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null && (hashed.getKeyFlags() & KeyFlags.CERTIFY_OTHER) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.CERTIFY_OTHER) != 0) { + return true; + } + } + + return false; + } + + public static boolean isCertificationKey(PGPSecretKey key) { + return isCertificationKey(key.getPublicKey()); + } + + public static String getAlgorithmInfo(PGPPublicKey key) { + return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength()); + } + + public static String getAlgorithmInfo(PGPSecretKey key) { + return getAlgorithmInfo(key.getPublicKey()); + } + + public static String getAlgorithmInfo(int algorithm, int keySize) { + String algorithmStr = null; + + switch (algorithm) { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: { + algorithmStr = "RSA"; + break; + } + + case PGPPublicKey.DSA: { + algorithmStr = "DSA"; + break; + } + + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: { + algorithmStr = "ElGamal"; + break; + } + + default: { + algorithmStr = "???"; + break; + } + } + return algorithmStr + ", " + keySize + "bit"; + } + + public static String convertFingerprintToHex(byte[] fp) { + String fingerPrint = ""; + for (int i = 0; i < fp.length; ++i) { + if (i != 0 && i % 10 == 0) { + fingerPrint += " "; + } else if (i != 0 && i % 2 == 0) { + fingerPrint += " "; + } + String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US); + while (chunk.length() < 2) { + chunk = "0" + chunk; + } + fingerPrint += chunk; + } + + return fingerPrint; + + } + + public static String getFingerPrint(Context context, long keyId) { + PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); + // if it is no public key get it from your own keys... + if (key == null) { + PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); + if (secretKey == null) { + Log.e(Constants.TAG, "Key could not be found!"); + return null; + } + key = secretKey.getPublicKey(); + } + + return convertFingerprintToHex(key.getFingerprint()); + } + + public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) { + return secretKey.isPrivateKeyEmpty(); + } + + public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) { + PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); + if (secretKey == null) { + Log.e(Constants.TAG, "Key could not be found!"); + return false; // could be a public key, assume it is not empty + } + return isSecretKeyPrivateEmpty(secretKey); + } + + public static String getSmallFingerPrint(long keyId) { + String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US); + while (fingerPrint.length() < 8) { + fingerPrint = "0" + fingerPrint; + } + return fingerPrint; + } + + public static String keyToHex(long keyId) { + return getSmallFingerPrint(keyId >> 32) + getSmallFingerPrint(keyId); + } + + public static long keyFromHex(String data) { + int len = data.length(); + String s2 = data.substring(len - 8); + String s1 = data.substring(0, len - 8); + return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpMain.java new file mode 100644 index 000000000..f6c9ebda8 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpMain.java @@ -0,0 +1,1884 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2010 Thialfihar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.pgp; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.regex.Pattern; + +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +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.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +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.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.util.HkpKeyServer; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.PositionAwareInputStream; +import org.sufficientlysecure.keychain.util.Primes; +import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.Environment; + +/** + * TODO: + * + * - Separate this file into different helpers + * + */ +public class PgpMain { + + static { + // Define Java Security Provider to be Bouncy Castle + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + + // Not BC due to the use of Spongy Castle for Android + public static final String SC = BouncyCastleProvider.PROVIDER_NAME; + public static final String BOUNCY_CASTLE_PROVIDER_NAME = SC; + + private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] { + SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5, + SymmetricKeyAlgorithmTags.TRIPLE_DES }; + private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1, + HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 }; + private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] { + CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP }; + + public static Pattern PGP_MESSAGE = Pattern.compile( + ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL); + + public static Pattern PGP_SIGNED_MESSAGE = Pattern + .compile( + ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", + Pattern.DOTALL); + + public static Pattern PGP_PUBLIC_KEY = Pattern.compile( + ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*", + Pattern.DOTALL); + + private static String mEditPassPhrase = null; + + public static class PgpGeneralException extends Exception { + static final long serialVersionUID = 0xf812773342L; + + public PgpGeneralException(String message) { + super(message); + } + } + + public static class NoAsymmetricEncryptionException extends Exception { + static final long serialVersionUID = 0xf812773343L; + + public NoAsymmetricEncryptionException() { + super(); + } + } + + public static void setEditPassPhrase(String passPhrase) { + mEditPassPhrase = passPhrase; + } + + public static String getEditPassPhrase() { + return mEditPassPhrase; + } + + public static void updateProgress(ProgressDialogUpdater progress, int message, int current, + int total) { + if (progress != null) { + progress.setProgress(message, current, total); + } + } + + public static void updateProgress(ProgressDialogUpdater progress, int current, int total) { + if (progress != null) { + progress.setProgress(current, total); + } + } + + /** + * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key + * when this key is the new masterkey. If a masterkey is supplied in the parameters + * PGPSecretKeyRing contains the masterkey and the new key as a subkey (certified by the + * masterkey). + * + * @param context + * @param algorithmChoice + * @param keySize + * @param passPhrase + * @param masterSecretKey + * @return + * @throws NoSuchAlgorithmException + * @throws PGPException + * @throws NoSuchProviderException + * @throws PgpGeneralException + * @throws InvalidAlgorithmParameterException + */ + public static PGPSecretKeyRing createKey(Context context, int algorithmChoice, int keySize, + String passPhrase, PGPSecretKey masterSecretKey) throws NoSuchAlgorithmException, + PGPException, NoSuchProviderException, PgpGeneralException, + InvalidAlgorithmParameterException { + + if (keySize < 512) { + throw new PgpGeneralException(context.getString(R.string.error_keySizeMinimum512bit)); + } + + if (passPhrase == null) { + passPhrase = ""; + } + + int algorithm = 0; + KeyPairGenerator keyGen = null; + + switch (algorithmChoice) { + case Id.choice.algorithm.dsa: { + keyGen = KeyPairGenerator.getInstance("DSA", BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + algorithm = PGPPublicKey.DSA; + break; + } + + case Id.choice.algorithm.elgamal: { + if (masterSecretKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_masterKeyMustNotBeElGamal)); + } + keyGen = KeyPairGenerator.getInstance("ElGamal", BOUNCY_CASTLE_PROVIDER_NAME); + BigInteger p = Primes.getBestPrime(keySize); + BigInteger g = new BigInteger("2"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + keyGen.initialize(elParams); + algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; + break; + } + + case Id.choice.algorithm.rsa: { + keyGen = KeyPairGenerator.getInstance("RSA", BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + + algorithm = PGPPublicKey.RSA_GENERAL; + break; + } + + default: { + throw new PgpGeneralException(context.getString(R.string.error_unknownAlgorithmChoice)); + } + } + + // build new key pair + PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + + // Build key encrypter and decrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + passPhrase.toCharArray()); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); + + PGPKeyRingGenerator ringGen = null; + PGPContentSignerBuilder certificationSignerBuilder = null; + if (masterSecretKey == null) { + certificationSignerBuilder = new JcaPGPContentSignerBuilder(keyPair.getPublicKey() + .getAlgorithm(), HashAlgorithmTags.SHA1); + + // build keyRing with only this one master key in it! + ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, keyPair, "", + sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); + } else { + PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); + PGPPrivateKey masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + + certificationSignerBuilder = new JcaPGPContentSignerBuilder(masterKeyPair + .getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // build keyRing with master key and new key as subkey (certified by masterkey) + ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, + "", sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); + + ringGen.addSubKey(keyPair); + } + + PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); + + return secKeyRing; + } + + public static void changeSecretKeyPassphrase(Context context, PGPSecretKeyRing keyRing, + String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) + throws IOException, PGPException, PGPException, NoSuchProviderException { + + updateProgress(progress, R.string.progress_buildingKey, 0, 100); + if (oldPassPhrase == null) { + oldPassPhrase = ""; + } + if (newPassPhrase == null) { + newPassPhrase = ""; + } + + PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword( + keyRing, + new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()), + new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey() + .getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray())); + + updateProgress(progress, R.string.progress_savingKeyRing, 50, 100); + + ProviderHelper.saveKeyRing(context, newKeyRing); + + updateProgress(progress, R.string.progress_done, 100, 100); + + } + + public static void buildSecretKey(Context context, ArrayList userIds, + ArrayList keys, ArrayList keysUsages, long masterKeyId, + String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) + throws PgpGeneralException, NoSuchProviderException, PGPException, + NoSuchAlgorithmException, SignatureException, IOException { + + Log.d(Constants.TAG, "userIds: " + userIds.toString()); + + updateProgress(progress, R.string.progress_buildingKey, 0, 100); + + if (oldPassPhrase == null) { + oldPassPhrase = ""; + } + if (newPassPhrase == null) { + newPassPhrase = ""; + } + + updateProgress(progress, R.string.progress_preparingMasterKey, 10, 100); + + int usageId = keysUsages.get(0); + boolean canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); + boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); + + String mainUserId = userIds.get(0); + + PGPSecretKey masterKey = keys.get(0); + + // this removes all userIds and certifications previously attached to the masterPublicKey + PGPPublicKey tmpKey = masterKey.getPublicKey(); + PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), + tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); + + // already done by code above: + // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); + // // Somehow, the PGPPublicKey already has an empty certification attached to it when the + // // keyRing is generated the first time, we remove that when it exists, before adding the + // new + // // ones + // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, + // ""); + // if (masterPublicKeyRmCert != null) { + // masterPublicKey = masterPublicKeyRmCert; + // } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); + + updateProgress(progress, R.string.progress_certifyingMasterKey, 20, 100); + + for (String userId : userIds) { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + } + + // TODO: cross-certify the master key with every sub key (APG 1) + + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + + PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA; + if (canEncrypt) { + keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + } + hashedPacketsGen.setKeyFlags(true, keyFlags); + + hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); + hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); + hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); + + // TODO: this doesn't work quite right yet (APG 1) + // if (keyEditor.getExpiryDate() != null) { + // GregorianCalendar creationDate = new GregorianCalendar(); + // creationDate.setTime(getCreationDate(masterKey)); + // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); + // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); + // if (numDays <= 0) { + // throw new GeneralException( + // context.getString(R.string.error_expiryMustComeAfterCreation)); + // } + // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); + // } + + updateProgress(progress, R.string.progress_buildingMasterKeyRing, 30, 100); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( + masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // Build key encrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + newPassPhrase.toCharArray()); + + PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), + unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); + + updateProgress(progress, R.string.progress_addingSubKeys, 40, 100); + + for (int i = 1; i < keys.size(); ++i) { + updateProgress(progress, 40 + 50 * (i - 1) / (keys.size() - 1), 100); + + PGPSecretKey subKey = keys.get(i); + PGPPublicKey subPublicKey = subKey.getPublicKey(); + + PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); + + // TODO: now used without algorithm and creation time?! (APG 1) + PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); + + hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + keyFlags = 0; + + usageId = keysUsages.get(i); + canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); + canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); + if (canSign) { + keyFlags |= KeyFlags.SIGN_DATA; + } + if (canEncrypt) { + keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + } + hashedPacketsGen.setKeyFlags(true, keyFlags); + + // TODO: this doesn't work quite right yet (APG 1) + // if (keyEditor.getExpiryDate() != null) { + // GregorianCalendar creationDate = new GregorianCalendar(); + // creationDate.setTime(getCreationDate(masterKey)); + // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); + // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); + // if (numDays <= 0) { + // throw new GeneralException( + // context.getString(R.string.error_expiryMustComeAfterCreation)); + // } + // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); + // } + + keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); + } + + PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); + PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); + + updateProgress(progress, R.string.progress_savingKeyRing, 90, 100); + + ProviderHelper.saveKeyRing(context, secretKeyRing); + ProviderHelper.saveKeyRing(context, publicKeyRing); + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + /** + * TODO: implement Id.return_value.updated as status when key already existed + * + * @param context + * @param keyring + * @return + */ + @SuppressWarnings("unchecked") + public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { + int status = Integer.MIN_VALUE; // out of bounds value (Id.return_value.*) + try { + if (keyring instanceof PGPSecretKeyRing) { + PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; + boolean save = true; + + for (PGPSecretKey testSecretKey : new IterableIterator( + secretKeyRing.getSecretKeys())) { + if (!testSecretKey.isMasterKey()) { + if (PgpHelper.isSecretKeyPrivateEmpty(testSecretKey)) { + // this is bad, something is very wrong... + save = false; + status = Id.return_value.bad; + } + } + } + + if (save) { + ProviderHelper.saveKeyRing(context, secretKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } else if (keyring instanceof PGPPublicKeyRing) { + PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; + ProviderHelper.saveKeyRing(context, publicKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } catch (IOException e) { + status = Id.return_value.error; + } + + return status; + } + + public static boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ArmoredOutputStream aos = new ArmoredOutputStream(bos); + try { + aos.write(keyring.getEncoded()); + aos.close(); + + String armouredKey = bos.toString("UTF-8"); + server.add(armouredKey); + + return true; + } catch (IOException e) { + return false; + } catch (AddKeyException e) { + // TODO: tell the user? + return false; + } finally { + try { + bos.close(); + } catch (IOException e) { + } + } + } + + public static Bundle importKeyRings(Context context, InputData data, + ProgressDialogUpdater progress) throws PgpGeneralException, FileNotFoundException, + PGPException, IOException { + Bundle returnData = new Bundle(); + + updateProgress(progress, R.string.progress_importingSecretKeys, 0, 100); + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + + PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream()); + + // need to have access to the bufferedInput, so we can reuse it for the possible + // PGPObject chunks after the first one, e.g. files with several consecutive ASCII + // armour blocks + BufferedInputStream bufferedInput = new BufferedInputStream(progressIn); + int newKeys = 0; + int oldKeys = 0; + int badKeys = 0; + try { + + // read all available blocks... (asc files can contain many blocks with BEGIN END) + while (bufferedInput.available() > 0) { + 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 keyring = (PGPKeyRing) obj; + + int status = Integer.MIN_VALUE; // out of bounds value + + status = storeKeyRingInCache(context, keyring); + + if (status == Id.return_value.error) { + throw new PgpGeneralException( + context.getString(R.string.error_savingKeys)); + } + + // update the counts to display to the user at the end + if (status == Id.return_value.updated) { + ++oldKeys; + } else if (status == Id.return_value.ok) { + ++newKeys; + } else if (status == Id.return_value.bad) { + ++badKeys; + } + + updateProgress(progress, + (int) (100 * progressIn.position() / data.getSize()), 100); + } else { + Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); + } + } + } + } catch (Exception e) { + Log.e(Constants.TAG, "Exception on parsing key file!", e); + } + + returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); + returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); + returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); + + updateProgress(progress, R.string.progress_done, 100, 100); + + return returnData; + } + + public static Bundle exportKeyRings(Context context, ArrayList keyRingMasterKeyIds, + int keyType, OutputStream outStream, ProgressDialogUpdater progress) + throws PgpGeneralException, FileNotFoundException, PGPException, IOException { + Bundle returnData = new Bundle(); + + if (keyRingMasterKeyIds.size() == 1) { + updateProgress(progress, R.string.progress_exportingKey, 0, 100); + } else { + updateProgress(progress, R.string.progress_exportingKeys, 0, 100); + } + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + + // export public keyrings... + ArmoredOutputStream outPub = new ArmoredOutputStream(outStream); + outPub.setHeader("Version", getFullVersion(context)); + + int numKeys = 0; + for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { + // double the needed time if exporting both public and secret parts + if (keyType == Id.type.secret_key) { + updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); + } else { + updateProgress(progress, i * 100 / keyRingMasterKeyIds.size(), 100); + } + + PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( + context, keyRingMasterKeyIds.get(i)); + + if (publicKeyRing != null) { + publicKeyRing.encode(outPub); + } + ++numKeys; + } + outPub.close(); + + // if we export secret keyrings, append all secret parts after the public parts + if (keyType == Id.type.secret_key) { + ArmoredOutputStream outSec = new ArmoredOutputStream(outStream); + outSec.setHeader("Version", getFullVersion(context)); + + for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { + updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); + + PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + context, keyRingMasterKeyIds.get(i)); + + if (secretKeyRing != null) { + secretKeyRing.encode(outSec); + } + } + outSec.close(); + } + + returnData.putInt(KeychainIntentService.RESULT_EXPORT, numKeys); + + updateProgress(progress, R.string.progress_done, 100, 100); + + return returnData; + } + + /** + * Encrypt and Sign data + * + * @param context + * @param progress + * @param data + * @param outStream + * @param useAsciiArmor + * @param compression + * @param encryptionKeyIds + * @param symmetricEncryptionAlgorithm + * @param encryptionPassphrase + * @param signatureKeyId + * @param signatureHashAlgorithm + * @param signatureForceV3 + * @param signaturePassphrase + * @throws IOException + * @throws PgpGeneralException + * @throws PGPException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws SignatureException + */ + public static void encryptAndSign(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean useAsciiArmor, int compression, + long[] encryptionKeyIds, String encryptionPassphrase, + int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, + boolean signatureForceV3, String signaturePassphrase) throws IOException, + PgpGeneralException, PGPException, NoSuchProviderException, NoSuchAlgorithmException, + SignatureException { + + if (encryptionKeyIds == null) { + encryptionKeyIds = new long[0]; + } + + ArmoredOutputStream armorOut = null; + OutputStream out = null; + OutputStream encryptOut = null; + if (useAsciiArmor) { + armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + out = armorOut; + } else { + out = outStream; + } + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (encryptionKeyIds.length == 0 && encryptionPassphrase == null) { + throw new PgpGeneralException( + context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); + } + + if (signatureKeyId != Id.key.none) { + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PgpHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassphrase == null) { + throw new PgpGeneralException( + context.getString(R.string.error_noSignaturePassPhrase)); + } + + updateProgress(progress, R.string.progress_extractingSignatureKey, 0, 100); + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + } + updateProgress(progress, R.string.progress_preparingStreams, 5, 100); + + // encrypt and compress input file content + JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder( + symmetricEncryptionAlgorithm).setProvider(BOUNCY_CASTLE_PROVIDER_NAME) + .setWithIntegrityPacket(true); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder); + + if (encryptionKeyIds.length == 0) { + // Symmetric encryption + Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption"); + + JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator( + encryptionPassphrase.toCharArray()); + cPk.addMethod(symmetricEncryptionGenerator); + } else { + // Asymmetric encryption + for (long id : encryptionKeyIds) { + PGPPublicKey key = PgpHelper.getEncryptPublicKey(context, id); + if (key != null) { + + JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator( + key); + cPk.addMethod(pubKeyEncryptionGenerator); + } + } + } + encryptOut = cPk.open(out, new byte[1 << 16]); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + if (signatureKeyId != Id.key.none) { + updateProgress(progress, R.string.progress_preparingSignature, 10, 100); + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (signatureForceV3) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); + + String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + } + + PGPCompressedDataGenerator compressGen = null; + BCPGOutputStream bcpgOut = null; + if (compression == Id.choice.compression.none) { + bcpgOut = new BCPGOutputStream(encryptOut); + } else { + compressGen = new PGPCompressedDataGenerator(compression); + bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut)); + } + if (signatureKeyId != Id.key.none) { + if (signatureForceV3) { + signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut); + } else { + signatureGenerator.generateOnePassVersion(false).encode(bcpgOut); + } + } + + PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); + // file name not needed, so empty string + OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(), + new byte[1 << 16]); + updateProgress(progress, R.string.progress_encrypting, 20, 100); + + long done = 0; + int n = 0; + byte[] buffer = new byte[1 << 16]; + InputStream in = data.getInputStream(); + while ((n = in.read(buffer)) > 0) { + pOut.write(buffer, 0, n); + if (signatureKeyId != Id.key.none) { + if (signatureForceV3) { + signatureV3Generator.update(buffer, 0, n); + } else { + signatureGenerator.update(buffer, 0, n); + } + } + done += n; + if (data.getSize() != 0) { + updateProgress(progress, (int) (20 + (95 - 20) * done / data.getSize()), 100); + } + } + + literalGen.close(); + + if (signatureKeyId != Id.key.none) { + updateProgress(progress, R.string.progress_generatingSignature, 95, 100); + if (signatureForceV3) { + signatureV3Generator.generate().encode(pOut); + } else { + signatureGenerator.generate().encode(pOut); + } + } + if (compressGen != null) { + compressGen.close(); + } + encryptOut.close(); + if (useAsciiArmor) { + armorOut.close(); + } + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static void signText(Context context, ProgressDialogUpdater progress, InputData data, + OutputStream outStream, long signatureKeyId, String signaturePassphrase, + int signatureHashAlgorithm, boolean forceV3Signature) throws PgpGeneralException, + PGPException, IOException, NoSuchAlgorithmException, SignatureException { + + ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (signatureKeyId == 0) { + armorOut.close(); + throw new PgpGeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PgpHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + armorOut.close(); + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassphrase == null) { + armorOut.close(); + throw new PgpGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + armorOut.close(); + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + updateProgress(progress, R.string.progress_preparingStreams, 0, 100); + + updateProgress(progress, R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey + .getPublicKey().getAlgorithm(), signatureHashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (forceV3Signature) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + updateProgress(progress, R.string.progress_signing, 40, 100); + + armorOut.beginClearText(signatureHashAlgorithm); + + InputStream inStream = data.getInputStream(); + final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); + + final byte[] newline = "\r\n".getBytes("UTF-8"); + + if (forceV3Signature) { + processLine(reader.readLine(), armorOut, signatureV3Generator); + } else { + processLine(reader.readLine(), armorOut, signatureGenerator); + } + + while (true) { + final String line = reader.readLine(); + + if (line == null) { + armorOut.write(newline); + break; + } + + armorOut.write(newline); + if (forceV3Signature) { + signatureV3Generator.update(newline); + processLine(line, armorOut, signatureV3Generator); + } else { + signatureGenerator.update(newline); + processLine(line, armorOut, signatureGenerator); + } + } + + armorOut.endClearText(); + + BCPGOutputStream bOut = new BCPGOutputStream(armorOut); + if (forceV3Signature) { + signatureV3Generator.generate().encode(bOut); + } else { + signatureGenerator.generate().encode(bOut); + } + armorOut.close(); + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static void generateSignature(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean armored, boolean binary, + long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, + boolean forceV3Signature) throws PgpGeneralException, PGPException, IOException, + NoSuchAlgorithmException, SignatureException { + + OutputStream out = null; + + // Ascii Armor (Base64) + ArmoredOutputStream armorOut = null; + if (armored) { + armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + out = armorOut; + } else { + out = outStream; + } + + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (signatureKeyId == 0) { + throw new PgpGeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PgpHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassPhrase == null) { + throw new PgpGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + updateProgress(progress, R.string.progress_preparingStreams, 0, 100); + + updateProgress(progress, R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; + if (binary) { + type = PGPSignature.BINARY_DOCUMENT; + } + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey + .getPublicKey().getAlgorithm(), hashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (forceV3Signature) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(type, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(type, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + updateProgress(progress, R.string.progress_signing, 40, 100); + + InputStream inStream = data.getInputStream(); + if (binary) { + byte[] buffer = new byte[1 << 16]; + int n = 0; + while ((n = inStream.read(buffer)) > 0) { + if (forceV3Signature) { + signatureV3Generator.update(buffer, 0, n); + } else { + signatureGenerator.update(buffer, 0, n); + } + } + } else { + final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream)); + final byte[] newline = "\r\n".getBytes("UTF-8"); + + while (true) { + final String line = reader.readLine(); + + if (line == null) { + break; + } + + if (forceV3Signature) { + processLine(line, null, signatureV3Generator); + signatureV3Generator.update(newline); + } else { + processLine(line, null, signatureGenerator); + signatureGenerator.update(newline); + } + } + } + + BCPGOutputStream bOut = new BCPGOutputStream(out); + if (forceV3Signature) { + signatureV3Generator.generate().encode(bOut); + } else { + signatureGenerator.generate().encode(bOut); + } + out.close(); + outStream.close(); + + if (progress != null) + progress.setProgress(R.string.progress_done, 100, 100); + } + + public static PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId, + String passphrase) throws PgpGeneralException, NoSuchAlgorithmException, + NoSuchProviderException, PGPException, SignatureException { + if (passphrase == null || passphrase.length() <= 0) { + throw new PgpGeneralException("Unable to obtain passphrase"); + } else { + PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); + + PGPSecretKey signingKey = PgpHelper.getCertificationKey(context, masterKeyId); + if (signingKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + + // TODO: SHA256 fixed? + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( + contentSignerBuilder); + + signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + PGPSignatureSubpacketVector packetVector = spGen.generate(); + signatureGenerator.setHashedSubpackets(packetVector); + + PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), + signatureGenerator.generate()); + pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey); + + return pubring; + } + } + + public static long getDecryptionKeyId(Context context, InputStream inputStream) + throws PgpGeneralException, NoAsymmetricEncryptionException, IOException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // the first object might be a PGP marker packet. + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new PgpGeneralException(context.getString(R.string.error_invalidData)); + } + + // TODO: currently we always only look at the first known key + // find the secret key + PGPSecretKey secretKey = null; + Iterator it = enc.getEncryptedDataObjects(); + boolean gotAsymmetricEncryption = false; + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + gotAsymmetricEncryption = true; + PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; + secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, pbe.getKeyID()); + if (secretKey != null) { + break; + } + } + } + + if (!gotAsymmetricEncryption) { + throw new NoAsymmetricEncryptionException(); + } + + if (secretKey == null) { + return Id.key.none; + } + + return secretKey.getKeyID(); + } + + public static boolean hasSymmetricEncryption(Context context, InputStream inputStream) + throws PgpGeneralException, IOException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // the first object might be a PGP marker packet. + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new PgpGeneralException(context.getString(R.string.error_invalidData)); + } + + Iterator it = enc.getEncryptedDataObjects(); + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPBEEncryptedData) { + return true; + } + } + + return false; + } + + public static Bundle decryptAndVerify(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, String passphrase, boolean assumeSymmetric) + throws IOException, PgpGeneralException, PGPException, SignatureException { + if (passphrase == null) { + passphrase = ""; + } + + Bundle returnData = new Bundle(); + InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + long signatureKeyId = 0; + + int currentProgress = 0; + if (progress != null) + progress.setProgress(R.string.progress_readingData, currentProgress, 100); + + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new PgpGeneralException(context.getString(R.string.error_invalidData)); + } + + InputStream clear = null; + PGPEncryptedData encryptedData = null; + + currentProgress += 5; + + // TODO: currently we always only look at the first known key or symmetric encryption, + // there might be more... + if (assumeSymmetric) { + PGPPBEEncryptedData pbe = null; + Iterator it = enc.getEncryptedDataObjects(); + // find secret key + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPBEEncryptedData) { + pbe = (PGPPBEEncryptedData) obj; + break; + } + } + + if (pbe == null) { + throw new PgpGeneralException( + context.getString(R.string.error_noSymmetricEncryptionPacket)); + } + + updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); + + PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(); + PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( + digestCalcProvider).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + passphrase.toCharArray()); + + clear = pbe.getDataStream(decryptorFactory); + + encryptedData = pbe; + currentProgress += 5; + } else { + if (progress != null) + progress.setProgress(R.string.progress_findingKey, currentProgress, 100); + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKey secretKey = null; + Iterator it = enc.getEncryptedDataObjects(); + // find secret key + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; + secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); + if (secretKey != null) { + pbe = encData; + break; + } + } + } + + if (secretKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_noSecretKeyFound)); + } + + currentProgress += 5; + updateProgress(progress, R.string.progress_extractingKey, currentProgress, 100); + PGPPrivateKey privateKey = null; + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + privateKey = secretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); + } + if (privateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + currentProgress += 5; + updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); + + PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey); + + clear = pbe.getDataStream(decryptorFactory); + + encryptedData = pbe; + currentProgress += 5; + } + + PGPObjectFactory plainFact = new PGPObjectFactory(clear); + Object dataChunk = plainFact.nextObject(); + PGPOnePassSignature signature = null; + PGPPublicKey signatureKey = null; + int signatureIndex = -1; + + if (dataChunk instanceof PGPCompressedData) { + if (progress != null) + progress.setProgress(R.string.progress_decompressingData, currentProgress, 100); + PGPObjectFactory fact = new PGPObjectFactory( + ((PGPCompressedData) dataChunk).getDataStream()); + dataChunk = fact.nextObject(); + plainFact = fact; + currentProgress += 10; + } + + if (dataChunk instanceof PGPOnePassSignatureList) { + if (progress != null) + progress.setProgress(R.string.progress_processingSignature, currentProgress, 100); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); + PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; + for (int i = 0; i < sigList.size(); ++i) { + signature = sigList.get(i); + signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); + if (signatureKeyId == 0) { + signatureKeyId = signature.getKeyID(); + } + if (signatureKey == null) { + signature = null; + } else { + signatureIndex = i; + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId( + context, signatureKeyId); + if (signKeyRing != null) { + userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signKeyRing)); + } + returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); + break; + } + } + + returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + + if (signature != null) { + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + signature.init(contentVerifierBuilderProvider, signatureKey); + } else { + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); + } + + dataChunk = plainFact.nextObject(); + currentProgress += 10; + } + + if (dataChunk instanceof PGPSignatureList) { + dataChunk = plainFact.nextObject(); + } + + if (dataChunk instanceof PGPLiteralData) { + if (progress != null) + progress.setProgress(R.string.progress_decrypting, currentProgress, 100); + PGPLiteralData literalData = (PGPLiteralData) dataChunk; + OutputStream out = outStream; + + byte[] buffer = new byte[1 << 16]; + InputStream dataIn = literalData.getInputStream(); + + int startProgress = currentProgress; + int endProgress = 100; + if (signature != null) { + endProgress = 90; + } else if (encryptedData.isIntegrityProtected()) { + endProgress = 95; + } + int n = 0; + int done = 0; + long startPos = data.getStreamPosition(); + while ((n = dataIn.read(buffer)) > 0) { + out.write(buffer, 0, n); + done += n; + if (signature != null) { + try { + signature.update(buffer, 0, n); + } catch (SignatureException e) { + returnData + .putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); + signature = null; + } + } + // unknown size, but try to at least have a moving, slowing down progress bar + currentProgress = startProgress + (endProgress - startProgress) * done + / (done + 100000); + if (data.getSize() - startPos == 0) { + currentProgress = endProgress; + } else { + currentProgress = (int) (startProgress + (endProgress - startProgress) + * (data.getStreamPosition() - startPos) / (data.getSize() - startPos)); + } + updateProgress(progress, currentProgress, 100); + } + + if (signature != null) { + if (progress != null) + progress.setProgress(R.string.progress_verifyingSignature, 90, 100); + PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); + PGPSignature messageSignature = signatureList.get(signatureIndex); + if (signature.verify(messageSignature)) { + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, true); + } else { + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); + } + } + } + + // TODO: add integrity somewhere + if (encryptedData.isIntegrityProtected()) { + if (progress != null) + progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100); + if (encryptedData.verify()) { + // passed + } else { + // failed + } + } else { + // no integrity check + } + + updateProgress(progress, R.string.progress_done, 100, 100); + return returnData; + } + + public static Bundle verifyText(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean lookupUnknownKey) throws IOException, + PgpGeneralException, PGPException, SignatureException { + Bundle returnData = new Bundle(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); + + updateProgress(progress, R.string.progress_done, 0, 100); + + // mostly taken from ClearSignedFileProcessor + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, aIn); + byte[] lineSep = getLineSeparator(); + + byte[] line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparator(line)); + out.write(lineSep); + + while (lookAhead != -1 && aIn.isClearText()) { + lookAhead = readInputLine(lineOut, lookAhead, aIn); + line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparator(line)); + out.write(lineSep); + } + + out.close(); + + byte[] clearText = out.toByteArray(); + outStream.write(clearText); + + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); + + updateProgress(progress, R.string.progress_processingSignature, 60, 100); + PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); + + PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); + if (sigList == null) { + throw new PgpGeneralException(context.getString(R.string.error_corruptData)); + } + PGPSignature signature = null; + long signatureKeyId = 0; + PGPPublicKey signatureKey = null; + for (int i = 0; i < sigList.size(); ++i) { + signature = sigList.get(i); + signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); + if (signatureKeyId == 0) { + signatureKeyId = signature.getKeyID(); + } + // if key is not known and we want to lookup unknown ones... + if (signatureKey == null && lookupUnknownKey) { + + returnData = new Bundle(); + returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true); + + // return directly now, decrypt will be done again after importing unknown key + return returnData; + } + + if (signatureKey == null) { + signature = null; + } else { + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, + signatureKeyId); + if (signKeyRing != null) { + userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signKeyRing)); + } + returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); + break; + } + } + + returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + + if (signature == null) { + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); + if (progress != null) + progress.setProgress(R.string.progress_done, 100, 100); + return returnData; + } + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + signature.init(contentVerifierBuilderProvider, signatureKey); + + InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); + + lookAhead = readInputLine(lineOut, sigIn); + + processLine(signature, lineOut.toByteArray()); + + if (lookAhead != -1) { + do { + lookAhead = readInputLine(lineOut, lookAhead, sigIn); + + signature.update((byte) '\r'); + signature.update((byte) '\n'); + + processLine(signature, lineOut.toByteArray()); + } while (lookAhead != -1); + } + + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); + + updateProgress(progress, R.string.progress_done, 100, 100); + return returnData; + } + + public static int getStreamContent(Context context, InputStream inStream) throws IOException { + InputStream in = PGPUtil.getDecoderStream(inStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + Object object = pgpF.nextObject(); + while (object != null) { + if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) { + return Id.content.keys; + } else if (object instanceof PGPEncryptedDataList) { + return Id.content.encrypted_data; + } + object = pgpF.nextObject(); + } + + return Id.content.unknown; + } + + private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, + final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException { + + if (pLine == null) { + return; + } + + final char[] chars = pLine.toCharArray(); + int len = chars.length; + + while (len > 0) { + if (!Character.isWhitespace(chars[len - 1])) { + break; + } + len--; + } + + final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); + + if (pArmoredOutput != null) { + pArmoredOutput.write(data); + } + pSignatureGenerator.update(data); + } + + private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, + final PGPV3SignatureGenerator pSignatureGenerator) throws IOException, + SignatureException { + + if (pLine == null) { + return; + } + + final char[] chars = pLine.toCharArray(); + int len = chars.length; + + while (len > 0) { + if (!Character.isWhitespace(chars[len - 1])) { + break; + } + len--; + } + + final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); + + if (pArmoredOutput != null) { + pArmoredOutput.write(data); + } + pSignatureGenerator.update(data); + } + + // taken from ClearSignedFileProcessor in BC + private static void processLine(PGPSignature sig, byte[] line) throws SignatureException, + IOException { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) { + sig.update(line, 0, length); + } + } + + private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) + throws IOException { + bOut.reset(); + + int lookAhead = -1; + int ch; + + while ((ch = fIn.read()) >= 0) { + bOut.write(ch); + if (ch == '\r' || ch == '\n') { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + + return lookAhead; + } + + private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) + throws IOException { + bOut.reset(); + + int ch = lookAhead; + + do { + bOut.write(ch); + if (ch == '\r' || ch == '\n') { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } while ((ch = fIn.read()) >= 0); + + if (ch < 0) { + lookAhead = -1; + } + + return lookAhead; + } + + private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) + throws IOException { + int lookAhead = fIn.read(); + + if (lastCh == '\r' && lookAhead == '\n') { + bOut.write(lookAhead); + lookAhead = fIn.read(); + } + + return lookAhead; + } + + private static int getLengthWithoutSeparator(byte[] line) { + int end = line.length - 1; + + while (end >= 0 && isLineEnding(line[end])) { + end--; + } + + return end + 1; + } + + private static boolean isLineEnding(byte b) { + return b == '\r' || b == '\n'; + } + + private static int getLengthWithoutWhiteSpace(byte[] line) { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) { + end--; + } + + return end + 1; + } + + private static boolean isWhiteSpace(byte b) { + return b == '\r' || b == '\n' || b == '\t' || b == ' '; + } + + private static byte[] getLineSeparator() { + String nl = System.getProperty("line.separator"); + byte[] nlBytes = new byte[nl.length()]; + + for (int i = 0; i != nlBytes.length; i++) { + nlBytes[i] = (byte) nl.charAt(i); + } + + return nlBytes; + } + + public static boolean isReleaseVersion(Context context) { + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); + if (pi.versionCode % 100 == 99) { + return true; + } else { + return false; + } + } catch (NameNotFoundException e) { + // impossible! + return false; + } + } + + public static String getVersion(Context context) { + String version = null; + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); + version = pi.versionName; + return version; + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Version could not be retrieved!", e); + return "0.0.0"; + } + } + + public static String getFullVersion(Context context) { + return "OpenPGP Keychain v" + getVersion(context); + } + + /** + * Generate a random filename + * + * @param length + * @return + */ + public static String generateRandomFilename(int length) { + SecureRandom random = new SecureRandom(); + + byte bytes[] = new byte[length]; + random.nextBytes(bytes); + String result = ""; + for (int i = 0; i < length; ++i) { + int v = (bytes[i] + 256) % 64; + if (v < 10) { + result += (char) ('0' + v); + } else if (v < 36) { + result += (char) ('A' + v - 10); + } else if (v < 62) { + result += (char) ('a' + v - 36); + } else if (v == 62) { + result += '_'; + } else if (v == 63) { + result += '.'; + } + } + return result; + } + + /** + * Go once through stream to get length of stream. The length is later used to display progress + * when encrypting/decrypting + * + * @param in + * @return + * @throws IOException + */ + public static long getLengthOfStream(InputStream in) throws IOException { + long size = 0; + long n = 0; + byte dummy[] = new byte[0x10000]; + while ((n = in.read(dummy)) > 0) { + size += n; + } + return size; + } + + /** + * Deletes file securely by overwriting it with random data before deleting it. + * + * TODO: Does this really help on flash storage? + * + * @param context + * @param progress + * @param file + * @throws FileNotFoundException + * @throws IOException + */ + public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file) + throws FileNotFoundException, IOException { + long length = file.length(); + SecureRandom random = new SecureRandom(); + RandomAccessFile raf = new RandomAccessFile(file, "rws"); + raf.seek(0); + raf.getFilePointer(); + byte[] data = new byte[1 << 16]; + int pos = 0; + String msg = context.getString(R.string.progress_deletingSecurely, file.getName()); + while (pos < length) { + if (progress != null) + progress.setProgress(msg, (int) (100 * pos / length), 100); + random.nextBytes(data); + raf.write(data); + pos += data.length; + } + raf.close(); + file.delete(); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpToX509.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpToX509.java new file mode 100644 index 000000000..546a32473 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpToX509.java @@ -0,0 +1,307 @@ +package org.sufficientlysecure.keychain.pgp; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.DateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.Vector; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.spongycastle.asn1.DERObjectIdentifier; +import org.spongycastle.asn1.x509.AuthorityKeyIdentifier; +import org.spongycastle.asn1.x509.BasicConstraints; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.SubjectKeyIdentifier; +import org.spongycastle.asn1.x509.X509Extensions; +import org.spongycastle.asn1.x509.X509Name; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.x509.X509V3CertificateGenerator; +import org.spongycastle.x509.extension.AuthorityKeyIdentifierStructure; +import org.spongycastle.x509.extension.SubjectKeyIdentifierStructure; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + +public class PgpToX509 { + public final static String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge"; + public final static String DN_COMMON_PART_OU = "OpenPGP Keychain cert"; + + /** + * Creates a self-signed certificate from a public and private key. The (critical) key-usage + * extension is set up with: digital signature, non-repudiation, key-encipherment, key-agreement + * and certificate-signing. The (non-critical) Netscape extension is set up with: SSL client and + * S/MIME. A URI subjectAltName may also be set up. + * + * @param pubKey + * public key + * @param privKey + * private key + * @param subject + * subject (and issuer) DN for this certificate, RFC 2253 format preferred. + * @param startDate + * date from which the certificate will be valid (defaults to current date and time + * if null) + * @param endDate + * date until which the certificate will be valid (defaults to current date and time + * if null) * + * @param subjAltNameURI + * URI to be placed in subjectAltName + * @return self-signed certificate + * @throws InvalidKeyException + * @throws SignatureException + * @throws NoSuchAlgorithmException + * @throws IllegalStateException + * @throws NoSuchProviderException + * @throws CertificateException + * @throws Exception + * + * @author Bruno Harbulot + */ + public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey, + X509Name subject, Date startDate, Date endDate, String subjAltNameURI) + throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, + SignatureException, CertificateException, NoSuchProviderException { + + X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator(); + + certGenerator.reset(); + /* + * Sets up the subject distinguished name. Since it's a self-signed certificate, issuer and + * subject are the same. + */ + certGenerator.setIssuerDN(subject); + certGenerator.setSubjectDN(subject); + + /* + * Sets up the validity dates. + */ + if (startDate == null) { + startDate = new Date(System.currentTimeMillis()); + } + certGenerator.setNotBefore(startDate); + if (endDate == null) { + endDate = new Date(startDate.getTime() + (365L * 24L * 60L * 60L * 1000L)); + Log.d(Constants.TAG, "end date is=" + DateFormat.getDateInstance().format(endDate)); + } + + certGenerator.setNotAfter(endDate); + + /* + * The serial-number of this certificate is 1. It makes sense because it's self-signed. + */ + certGenerator.setSerialNumber(BigInteger.ONE); + /* + * Sets the public-key to embed in this certificate. + */ + certGenerator.setPublicKey(pubKey); + /* + * Sets the signature algorithm. + */ + String pubKeyAlgorithm = pubKey.getAlgorithm(); + if (pubKeyAlgorithm.equals("DSA")) { + certGenerator.setSignatureAlgorithm("SHA1WithDSA"); + } else if (pubKeyAlgorithm.equals("RSA")) { + certGenerator.setSignatureAlgorithm("SHA1WithRSAEncryption"); + } else { + RuntimeException re = new RuntimeException("Algorithm not recognised: " + + pubKeyAlgorithm); + Log.e(Constants.TAG, re.getMessage(), re); + throw re; + } + + /* + * Adds the Basic Constraint (CA: true) extension. + */ + certGenerator.addExtension(X509Extensions.BasicConstraints, true, + new BasicConstraints(true)); + + /* + * Adds the subject key identifier extension. + */ + SubjectKeyIdentifier subjectKeyIdentifier = new SubjectKeyIdentifierStructure(pubKey); + certGenerator + .addExtension(X509Extensions.SubjectKeyIdentifier, false, subjectKeyIdentifier); + + /* + * Adds the authority key identifier extension. + */ + AuthorityKeyIdentifier authorityKeyIdentifier = new AuthorityKeyIdentifierStructure(pubKey); + certGenerator.addExtension(X509Extensions.AuthorityKeyIdentifier, false, + authorityKeyIdentifier); + + /* + * Adds the subject alternative-name extension. + */ + if (subjAltNameURI != null) { + GeneralNames subjectAltNames = new GeneralNames(new GeneralName( + GeneralName.uniformResourceIdentifier, subjAltNameURI)); + certGenerator.addExtension(X509Extensions.SubjectAlternativeName, false, + subjectAltNames); + } + + /* + * Creates and sign this certificate with the private key corresponding to the public key of + * the certificate (hence the name "self-signed certificate"). + */ + X509Certificate cert = certGenerator.generate(privKey); + + /* + * Checks that this certificate has indeed been correctly signed. + */ + cert.verify(pubKey); + + return cert; + } + + /** + * Creates a self-signed certificate from a PGP Secret Key. + * + * @param pgpSecKey + * PGP Secret Key (from which one can extract the public and private keys and other + * attributes). + * @param pgpPrivKey + * PGP Private Key corresponding to the Secret Key (password callbacks should be done + * before calling this method) + * @param subjAltNameURI + * optional URI to embed in the subject alternative-name + * @return self-signed certificate + * @throws PGPException + * @throws NoSuchProviderException + * @throws InvalidKeyException + * @throws NoSuchAlgorithmException + * @throws SignatureException + * @throws CertificateException + * + * @author Bruno Harbulot + */ + public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey, + PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException, + NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException, + SignatureException, CertificateException { + // get public key from secret key + PGPPublicKey pgpPubKey = pgpSecKey.getPublicKey(); + + // LOGGER.info("Key ID: " + Long.toHexString(pgpPubKey.getKeyID() & 0xffffffffL)); + + /* + * The X.509 Name to be the subject DN is prepared. The CN is extracted from the Secret Key + * user ID. + */ + Vector x509NameOids = new Vector(); + Vector x509NameValues = new Vector(); + + x509NameOids.add(X509Name.O); + x509NameValues.add(DN_COMMON_PART_O); + + x509NameOids.add(X509Name.OU); + x509NameValues.add(DN_COMMON_PART_OU); + + for (@SuppressWarnings("unchecked") + Iterator it = (Iterator) pgpSecKey.getUserIDs(); it.hasNext();) { + Object attrib = it.next(); + x509NameOids.add(X509Name.CN); + x509NameValues.add("CryptoCall"); + // x509NameValues.add(attrib.toString()); + } + + /* + * Currently unused. + */ + Log.d(Constants.TAG, "User attributes: "); + for (@SuppressWarnings("unchecked") + Iterator it = (Iterator) pgpSecKey.getUserAttributes(); it.hasNext();) { + Object attrib = it.next(); + Log.d(Constants.TAG, " - " + attrib + " -- " + attrib.getClass()); + } + + X509Name x509name = new X509Name(x509NameOids, x509NameValues); + + Log.d(Constants.TAG, "Subject DN: " + x509name); + + /* + * To check the signature from the certificate on the recipient side, the creation time + * needs to be embedded in the certificate. It seems natural to make this creation time be + * the "not-before" date of the X.509 certificate. Unlimited PGP keys have a validity of 0 + * second. In this case, the "not-after" date will be the same as the not-before date. This + * is something that needs to be checked by the service receiving this certificate. + */ + Date creationTime = pgpPubKey.getCreationTime(); + Log.d(Constants.TAG, + "pgp pub key creation time=" + DateFormat.getDateInstance().format(creationTime)); + Log.d(Constants.TAG, "pgp valid seconds=" + pgpPubKey.getValidSeconds()); + Date validTo = null; + if (pgpPubKey.getValidSeconds() > 0) { + validTo = new Date(creationTime.getTime() + 1000L * pgpPubKey.getValidSeconds()); + } + + X509Certificate selfSignedCert = createSelfSignedCert( + pgpPubKey.getKey(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME), pgpPrivKey.getKey(), + x509name, creationTime, validTo, subjAltNameURI); + + return selfSignedCert; + } + + /** + * This is a password callback handler that will fill in a password automatically. Useful to + * configure passwords in advance, but should be used with caution depending on how much you + * allow passwords to be stored within your application. + * + * @author Bruno Harbulot. + * + */ + public final static class PredefinedPasswordCallbackHandler implements CallbackHandler { + + private char[] password; + private String prompt; + + public PredefinedPasswordCallbackHandler(String password) { + this(password == null ? null : password.toCharArray(), null); + } + + public PredefinedPasswordCallbackHandler(char[] password) { + this(password, null); + } + + public PredefinedPasswordCallbackHandler(String password, String prompt) { + this(password == null ? null : password.toCharArray(), prompt); + } + + public PredefinedPasswordCallbackHandler(char[] password, String prompt) { + this.password = password; + this.prompt = prompt; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof PasswordCallback) { + PasswordCallback pwCallback = (PasswordCallback) callback; + if ((this.prompt == null) || (this.prompt.equals(pwCallback.getPrompt()))) { + pwCallback.setPassword(this.password); + } + } else { + throw new UnsupportedCallbackException(callback, "Unrecognised callback."); + } + } + } + + protected final Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 18dee5b9e..27410d6e0 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,9 +29,9 @@ import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.helper.PgpConversionHelper; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 62f42726a..d7ddedce3 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -36,10 +36,10 @@ import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.OtherHelper; -import org.sufficientlysecure.keychain.helper.PgpConversionHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; -import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpMain.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.HkpKeyServer; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 600af18c9..466d087a2 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -28,8 +28,8 @@ import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper; import android.app.AlarmManager; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java index 34bd72564..8eb34b6e8 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java @@ -26,7 +26,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.SelectSecretKeyActivity; import org.sufficientlysecure.keychain.util.KeyValueSpinnerAdapter; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java index beb31d081..94e4004c6 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java @@ -29,9 +29,9 @@ import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openssl.PEMWriter; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; -import org.sufficientlysecure.keychain.helper.PgpToX509; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpToX509; import org.sufficientlysecure.keychain.util.Log; import android.content.Intent; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java index 862ac3536..200675288 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java @@ -31,8 +31,8 @@ import org.openintents.openpgp.OpenPgpSignatureResult; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.PgpMain; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.PassphraseCacheService; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java index c5bb1e4bd..cf64fb71d 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java @@ -24,7 +24,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java index e07fa9d63..ad5b15744 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -32,8 +32,8 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.FileHelper; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 84b648c19..0c1536d8d 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -27,10 +27,10 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.helper.PgpConversionHelper; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; -import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpMain.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java index ee181b03a..3e4b2bcfb 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -30,9 +30,9 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.FileHelper; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java index 76d16700e..1b7d80b36 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java index a1de4b0bc..84b3d6689 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java index 7c3112693..458603f84 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java @@ -22,8 +22,8 @@ import java.util.List; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java index e8f989dff..7e7af7feb 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java @@ -22,8 +22,8 @@ import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpMain; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.pgp.PgpMain; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -41,7 +41,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; - import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index fd953ecdd..a4a88fcdf 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -21,7 +21,7 @@ import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.R; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java index 1930b2ab9..eebda1799 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui.dialog; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.ui.KeyServerQueryActivity; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.R; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java index 2da86862c..c3a3dee8e 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -24,9 +24,9 @@ import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; -import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpMain.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.util.Log; @@ -43,7 +43,6 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v4.app.DialogFragment; - import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java index ece72c010..42c5ddef7 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java @@ -32,7 +32,7 @@ import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.PositionAwareInputStream; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 338ffe570..729123e13 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui.widget; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.util.Choice; import org.sufficientlysecure.keychain.R; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java index 75c764540..e41db4f2f 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui.widget; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.helper.OtherHelper; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 932a45127..d201780f2 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -23,7 +23,7 @@ import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.PgpConversionHelper; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java index 3612f485e..b6d2949d3 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ui.widget; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.helper.OtherHelper; -import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.R; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 586a03498..c414dfd86 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -43,8 +43,8 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; -import org.sufficientlysecure.keychain.helper.PgpHelper; -import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpMain; import android.text.Html; -- cgit v1.2.3