diff options
author | Dominik Schürmann <dominik@dominikschuermann.de> | 2013-09-15 16:42:08 +0200 |
---|---|---|
committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2013-09-15 16:42:08 +0200 |
commit | 5aebd115d4a7a8ba7d538621bbf9e88ef941f48c (patch) | |
tree | 14398fad82c0d244d19028cafb07ad4234e994b9 /OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpImportExport.java | |
parent | 121f8aaca040cd54d8182f0ab9adba961bdfde6d (diff) | |
download | open-keychain-5aebd115d4a7a8ba7d538621bbf9e88ef941f48c.tar.gz open-keychain-5aebd115d4a7a8ba7d538621bbf9e88ef941f48c.tar.bz2 open-keychain-5aebd115d4a7a8ba7d538621bbf9e88ef941f48c.zip |
Put PgpMain methods in separate opbject classes, handle passphrase dialog in EditKey not in SecretKeyList
Diffstat (limited to 'OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpImportExport.java')
-rw-r--r-- | OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpImportExport.java | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpImportExport.java new file mode 100644 index 000000000..c4ce3d29a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * + * 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.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPUtil; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +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.ProgressDialogUpdater; + +import android.content.Context; +import android.os.Bundle; +import android.os.Environment; + +public class PgpImportExport { + private Context mContext; + private ProgressDialogUpdater mProgress; + + public PgpImportExport(Context context, ProgressDialogUpdater progress) { + super(); + this.mContext = context; + this.mProgress = progress; + } + + public void updateProgress(int message, int current, int total) { + if (mProgress != null) { + mProgress.setProgress(message, current, total); + } + } + + public void updateProgress(int current, int total) { + if (mProgress != null) { + mProgress.setProgress(current, total); + } + } + + public 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 Bundle importKeyRings(InputData data) throws PgpGeneralException, FileNotFoundException, + PGPException, IOException { + Bundle returnData = new Bundle(); + + updateProgress(R.string.progress_importingSecretKeys, 0, 100); + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException( + mContext.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(keyring); + + if (status == Id.return_value.error) { + throw new PgpGeneralException( + mContext.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((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(R.string.progress_done, 100, 100); + + return returnData; + } + + public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType, + OutputStream outStream) throws PgpGeneralException, FileNotFoundException, + PGPException, IOException { + Bundle returnData = new Bundle(); + + if (keyRingMasterKeyIds.size() == 1) { + updateProgress(R.string.progress_exportingKey, 0, 100); + } else { + updateProgress(R.string.progress_exportingKeys, 0, 100); + } + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException( + mContext.getString(R.string.error_externalStorageNotReady)); + } + + // export public keyrings... + ArmoredOutputStream outPub = new ArmoredOutputStream(outStream); + outPub.setHeader("Version", PgpHelper.getFullVersion(mContext)); + + 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(i * 100 / keyRingMasterKeyIds.size() / 2, 100); + } else { + updateProgress(i * 100 / keyRingMasterKeyIds.size(), 100); + } + + PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( + mContext, 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", PgpHelper.getFullVersion(mContext)); + + for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { + updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100); + + PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + mContext, keyRingMasterKeyIds.get(i)); + + if (secretKeyRing != null) { + secretKeyRing.encode(outSec); + } + } + outSec.close(); + } + + returnData.putInt(KeychainIntentService.RESULT_EXPORT, numKeys); + + updateProgress(R.string.progress_done, 100, 100); + + return returnData; + } + + /** + * TODO: implement Id.return_value.updated as status when key already existed + * + * @param context + * @param keyring + * @return + */ + @SuppressWarnings("unchecked") + public int storeKeyRingInCache(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<PGPSecretKey>( + secretKeyRing.getSecretKeys())) { + if (!testSecretKey.isMasterKey()) { + if (PgpKeyHelper.isSecretKeyPrivateEmpty(testSecretKey)) { + // this is bad, something is very wrong... + save = false; + status = Id.return_value.bad; + } + } + } + + if (save) { + ProviderHelper.saveKeyRing(mContext, secretKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } else if (keyring instanceof PGPPublicKeyRing) { + PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; + ProviderHelper.saveKeyRing(mContext, publicKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } catch (IOException e) { + status = Id.return_value.error; + } + + return status; + } + +} |