diff options
author | Thialfihar <thialfihar@gmail.com> | 2010-12-25 14:00:25 +0000 |
---|---|---|
committer | Thialfihar <thialfihar@gmail.com> | 2010-12-25 14:00:25 +0000 |
commit | 428cf39ba39314a193bc70930ca45317c4543404 (patch) | |
tree | b8a4e76983f92e310f1f6d81d7fd339fd0d54fa8 | |
parent | 01889c405fd733c6a55eb7c9ef1f188b4cc95637 (diff) | |
download | open-keychain-428cf39ba39314a193bc70930ca45317c4543404.tar.gz open-keychain-428cf39ba39314a193bc70930ca45317c4543404.tar.bz2 open-keychain-428cf39ba39314a193bc70930ca45317c4543404.zip |
added an Intent and functionality to generate detached signatures
-rw-r--r-- | AndroidManifest.xml | 1 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/Apg.java | 132 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/DataSource.java | 10 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/EncryptActivity.java | 44 |
4 files changed, 177 insertions, 10 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0f57cd80c..f496d0f41 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -124,6 +124,7 @@ <action android:name="org.thialfihar.android.apg.intent.ENCRYPT" /> <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" /> <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN" /> + <action android:name="org.thialfihar.android.apg.intent.GENERATE_SIGNATURE" /> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="*/*"/> </intent-filter> diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java index 91532b64f..d3be3907c 100644 --- a/src/org/thialfihar/android/apg/Apg.java +++ b/src/org/thialfihar/android/apg/Apg.java @@ -118,6 +118,7 @@ public class Apg { public static final String IMPORT = "org.thialfihar.android.apg.intent.IMPORT"; public static final String LOOK_UP_KEY_ID = "org.thialfihar.android.apg.intent.LOOK_UP_KEY_ID"; public static final String LOOK_UP_KEY_ID_AND_RETURN = "org.thialfihar.android.apg.intent.LOOK_UP_KEY_ID_AND_RETURN"; + public static final String GENERATE_SIGNATURE = "org.thialfihar.android.apg.intent.GENERATE_SIGNATURE"; } public static final String EXTRA_TEXT = "text"; @@ -133,6 +134,8 @@ public class Apg { public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId"; public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess"; public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown"; + public static final String EXTRA_SIGNATURE_DATA = "signatureData"; + public static final String EXTRA_SIGNATURE_TEXT = "signatureText"; public static final String EXTRA_USER_ID = "userId"; public static final String EXTRA_USER_IDS = "userIds"; public static final String EXTRA_KEY_ID = "keyId"; @@ -1426,6 +1429,127 @@ public class Apg { progress.setProgress(R.string.progress_done, 100, 100); } + public static void generateSignature(Context context, + InputData data, OutputStream outStream, + boolean armored, boolean binary, + long signatureKeyId, String signaturePassPhrase, + int hashAlgorithm, + boolean forceV3Signature, + ProgressDialogUpdater progress) + throws GeneralException, PGPException, IOException, NoSuchAlgorithmException, + SignatureException { + Security.addProvider(new BouncyCastleProvider()); + + ArmoredOutputStream armorOut = null; + OutputStream out = 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 GeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = getSecretKeyRing(signatureKeyId); + signingKey = getSigningKey(signatureKeyId); + if (signingKey == null) { + throw new GeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassPhrase == null) { + throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + signaturePrivateKey = + signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(), + new BouncyCastleProvider()); + if (signaturePrivateKey == null) { + throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey)); + } + progress.setProgress(R.string.progress_preparingStreams, 0, 100); + + progress.setProgress(R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; + if (binary) { + type = PGPSignature.BINARY_DOCUMENT; + } + + if (forceV3Signature) { + signatureV3Generator = + new PGPV3SignatureGenerator(signingKey.getPublicKey().getAlgorithm(), + hashAlgorithm, + new BouncyCastleProvider()); + signatureV3Generator.initSign(type, signaturePrivateKey); + } else { + signatureGenerator = + new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(), + hashAlgorithm, + new BouncyCastleProvider()); + signatureGenerator.initSign(type, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = getMainUserId(getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + progress.setProgress(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(); + + progress.setProgress(R.string.progress_done, 100, 100); + } + public static long getDecryptionKeyId(Context context, InputData data) throws GeneralException, NoAsymmetricEncryptionException, IOException { InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); @@ -1867,7 +1991,9 @@ public class Apg { final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); - pArmoredOutput.write(data); + if (pArmoredOutput != null) { + pArmoredOutput.write(data); + } pSignatureGenerator.update(data); } @@ -1892,7 +2018,9 @@ public class Apg { final byte[] data = pLine.substring(0, len).getBytes("UTF-8"); - pArmoredOutput.write(data); + if (pArmoredOutput != null) { + pArmoredOutput.write(data); + } pSignatureGenerator.update(data); } diff --git a/src/org/thialfihar/android/apg/DataSource.java b/src/org/thialfihar/android/apg/DataSource.java index 3ad1d0b07..4ce5c5f7b 100644 --- a/src/org/thialfihar/android/apg/DataSource.java +++ b/src/org/thialfihar/android/apg/DataSource.java @@ -36,10 +36,20 @@ public class DataSource { public void setText(String text) { mText = text; + mData = null; } public void setData(byte[] data) { mData = data; + mText = null; + } + + public boolean isText() { + return mText != null; + } + + public boolean isBinary() { + return mData != null; } public InputData getInputData(Context context, boolean withSize) diff --git a/src/org/thialfihar/android/apg/EncryptActivity.java b/src/org/thialfihar/android/apg/EncryptActivity.java index ac9550788..066b0b483 100644 --- a/src/org/thialfihar/android/apg/EncryptActivity.java +++ b/src/org/thialfihar/android/apg/EncryptActivity.java @@ -101,11 +101,15 @@ public class EncryptActivity extends BaseActivity { private DataSource mDataSource = null; private DataDestination mDataDestination = null; + private boolean mGenerateSignature = false; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.encrypt); + mGenerateSignature = false; + mSource = (ViewFlipper) findViewById(R.id.source); mSourceLabel = (TextView) findViewById(R.id.sourceLabel); mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious); @@ -271,17 +275,25 @@ public class EncryptActivity extends BaseActivity { mIntent = getIntent(); if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || - Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { + Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || + Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { mContentUri = mIntent.getData(); Bundle extras = mIntent.getExtras(); if (extras == null) { extras = new Bundle(); } - if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { + if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || + Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { mReturnResult = true; } + if (Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { + mGenerateSignature = true; + mOverrideAsciiArmour = true; + mAsciiArmourDemand = false; + } + if (extras.containsKey(Apg.EXTRA_ASCII_ARMOUR)) { mAsciiArmourDemand = extras.getBoolean(Apg.EXTRA_ASCII_ARMOUR, true); mOverrideAsciiArmour = true; @@ -338,7 +350,8 @@ public class EncryptActivity extends BaseActivity { } if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || - Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { + Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || + Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { if (textData != null) { mMessage.setText(textData); } @@ -660,7 +673,14 @@ public class EncryptActivity extends BaseActivity { useAsciiArmour = mAsciiArmourDemand; } - if (signOnly) { + if (mGenerateSignature) { + Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(), + getSecretKeyId(), + Apg.getCachedPassPhrase(getSecretKeyId()), + mPreferences.getDefaultHashAlgorithm(), + mPreferences.getForceV3Signatures(), + this); + } else if (signOnly) { Apg.signText(this, in, out, getSecretKeyId(), Apg.getCachedPassPhrase(getSecretKeyId()), mPreferences.getDefaultHashAlgorithm(), @@ -680,11 +700,19 @@ public class EncryptActivity extends BaseActivity { out.close(); if (mEncryptTarget != Id.target.file) { if (useAsciiArmour) { - data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, - new String(((ByteArrayOutputStream)out).toByteArray())); + String extraData = new String(((ByteArrayOutputStream)out).toByteArray()); + if (mGenerateSignature) { + data.putString(Apg.EXTRA_SIGNATURE_TEXT, extraData); + } else { + data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, extraData); + } } else { - data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, - ((ByteArrayOutputStream)out).toByteArray()); + byte extraData[] = ((ByteArrayOutputStream)out).toByteArray(); + if (mGenerateSignature) { + data.putByteArray(Apg.EXTRA_SIGNATURE_DATA, extraData); + } else { + data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, extraData); + } } } } catch (IOException e) { |