aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2013-09-15 15:20:15 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2013-09-15 15:20:15 +0200
commit312b735fbd0303a49e3d2efbefd2076c432f276f (patch)
tree5269fe33f479349f5f71edcecd266b3fa70201d3 /OpenPGP-Keychain
parent1e188ee2fa0c0573d523bf78811fa05c3e5bbea5 (diff)
downloadopen-keychain-312b735fbd0303a49e3d2efbefd2076c432f276f.tar.gz
open-keychain-312b735fbd0303a49e3d2efbefd2076c432f276f.tar.bz2
open-keychain-312b735fbd0303a49e3d2efbefd2076c432f276f.zip
Extended api
Diffstat (limited to 'OpenPGP-Keychain')
-rw-r--r--OpenPGP-Keychain/AndroidManifest.xml51
-rw-r--r--OpenPGP-Keychain/libs/scpkix-jdk15on-1.47.0.2.jarbin0 -> 601103 bytes
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java307
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl32
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java139
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java125
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl (renamed from OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl)16
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl48
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java45
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java (renamed from OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteApiService.java)18
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java (renamed from OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpServiceActivity.java)14
11 files changed, 548 insertions, 247 deletions
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 47ff181b3..6febe57f4 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -358,29 +358,6 @@
android:name="org.sufficientlysecure.keychain.service.KeychainIntentService"
android:exported="false" />
- <!-- TODO: Make this extended API -->
-
-
- <!-- <meta-data -->
- <!-- android:name="api_version" -->
- <!-- android:value="3" /> -->
- <!-- </service> -->
- <!-- <service -->
- <!-- android:name="org.sufficientlysecure.keychain.service.KeychainKeyService" -->
- <!-- android:enabled="true" -->
- <!-- android:exported="true" -->
- <!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_KEYS" -->
- <!-- android:process=":remotekeys" > -->
- <!-- <intent-filter> -->
- <!-- <action android:name="org.sufficientlysecure.keychain.service.IKeychainKeyService" /> -->
- <!-- </intent-filter> -->
-
-
- <!-- <meta-data -->
- <!-- android:name="api_version" -->
- <!-- android:value="3" /> -->
- <!-- </service> -->
-
<provider
android:name="org.sufficientlysecure.keychain.provider.KeychainProvider"
android:authorities="org.sufficientlysecure.keychain.provider"
@@ -393,15 +370,15 @@
<!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
- <!-- OpenPGP API internal classes (not exported) -->
+ <!-- OpenPGP Remote API internal classes (not exported) -->
<activity
- android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpServiceActivity"
+ android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
android:exported="false"
android:label="@string/app_name"
android:launchMode="singleTop"
- android:process=":openpgp_api"
- android:taskAffinity=":openpgp_api" />
+ android:process=":remote_api"
+ android:taskAffinity=":remote_api" />
<activity
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -412,13 +389,13 @@
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:exported="false" />
- <!-- OpenPGP API -->
+ <!-- OpenPGP Remote API -->
<service
android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpService"
android:enabled="true"
android:exported="true"
- android:process=":openpgp_api" >
+ android:process=":remote_api" >
<intent-filter>
<action android:name="org.openintents.openpgp.IOpenPgpService" />
</intent-filter>
@@ -427,6 +404,22 @@
android:name="api_version"
android:value="1" />
</service>
+
+ <!-- Extended Remote API -->
+
+ <service
+ android:name="org.sufficientlysecure.keychain.service.remote.ExtendedApiService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":remote_api" >
+ <intent-filter>
+ <action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />
+ </intent-filter>
+
+ <meta-data
+ android:name="api_version"
+ android:value="1" />
+ </service>
</application>
</manifest> \ No newline at end of file
diff --git a/OpenPGP-Keychain/libs/scpkix-jdk15on-1.47.0.2.jar b/OpenPGP-Keychain/libs/scpkix-jdk15on-1.47.0.2.jar
new file mode 100644
index 000000000..e47eb02fc
--- /dev/null
+++ b/OpenPGP-Keychain/libs/scpkix-jdk15on-1.47.0.2.jar
Binary files differ
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java
new file mode 100644
index 000000000..34815a53d
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpToX509.java
@@ -0,0 +1,307 @@
+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<DERObjectIdentifier> x509NameOids = new Vector<DERObjectIdentifier>();
+ Vector<String> x509NameValues = new Vector<String>();
+
+ x509NameOids.add(X509Name.O);
+ x509NameValues.add(DN_COMMON_PART_O);
+
+ x509NameOids.add(X509Name.OU);
+ x509NameValues.add(DN_COMMON_PART_OU);
+
+ for (@SuppressWarnings("unchecked")
+ Iterator<Object> it = (Iterator<Object>) 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<Object> it = (Iterator<Object>) 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/service/IKeychainKeyService.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl
deleted file mode 100644
index ecea2b8ff..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * 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.service;
-
-import org.sufficientlysecure.keychain.service.handler.IKeychainGetKeyringsHandler;
-
-/**
- * All methods are oneway, which means they are asynchronous and non-blocking.
- * Results are returned into given Handler, which has to be implemented on client side.
- */
-interface IKeychainKeyService {
-
- oneway void getPublicKeyRings(in long[] masterKeyIds, in boolean asAsciiArmoredStringArray,
- in IKeychainGetKeyringsHandler handler);
-
- oneway void getSecretKeyRings(in long[] masterKeyIds, in boolean asAsciiArmoredStringArray,
- in IKeychainGetKeyringsHandler handler);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java
deleted file mode 100644
index e43dca12f..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.service;
-
-import java.util.ArrayList;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.service.IKeychainKeyService;
-import org.sufficientlysecure.keychain.service.handler.IKeychainGetKeyringsHandler;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-public class KeychainKeyService extends Service {
- Context mContext;
-
- @Override
- public void onCreate() {
- super.onCreate();
- mContext = this;
- Log.d(Constants.TAG, "ApgKeyService, onCreate()");
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(Constants.TAG, "ApgKeyService, onDestroy()");
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- /**
- * Synchronized implementation of getPublicKeyRings
- */
- private synchronized void getPublicKeyRingsSafe(long[] masterKeyIds,
- boolean asAsciiArmoredStringArray, IKeychainGetKeyringsHandler handler)
- throws RemoteException {
- if (asAsciiArmoredStringArray) {
- ArrayList<String> output = ProviderHelper.getPublicKeyRingsAsArmoredString(mContext,
- masterKeyIds);
-
- handler.onSuccess(null, output);
- } else {
- byte[] outputBytes = ProviderHelper
- .getPublicKeyRingsAsByteArray(mContext, masterKeyIds);
- handler.onSuccess(outputBytes, null);
- }
- }
-
- /**
- * Synchronized implementation of getSecretKeyRings
- */
- private synchronized void getSecretKeyRingsSafe(long[] masterKeyIds,
- boolean asAsciiArmoredStringArray, IKeychainGetKeyringsHandler handler)
- throws RemoteException {
- if (asAsciiArmoredStringArray) {
- ArrayList<String> output = ProviderHelper.getSecretKeyRingsAsArmoredString(mContext,
- masterKeyIds);
-
- handler.onSuccess(null, output);
- } else {
- byte[] outputBytes = ProviderHelper
- .getSecretKeyRingsAsByteArray(mContext, masterKeyIds);
- handler.onSuccess(outputBytes, null);
- }
-
- }
-
- /**
- * This is the implementation of the interface IApgKeyService. All methods are oneway, meaning
- * asynchronous and return to the client using handlers.
- *
- * The real PGP code is located in PGPMain.
- */
- private final IKeychainKeyService.Stub mBinder = new IKeychainKeyService.Stub() {
-
- @Override
- public void getPublicKeyRings(long[] masterKeyIds, boolean asAsciiArmoredStringArray,
- IKeychainGetKeyringsHandler handler) throws RemoteException {
- getPublicKeyRingsSafe(masterKeyIds, asAsciiArmoredStringArray, handler);
- }
-
- @Override
- public void getSecretKeyRings(long[] masterKeyIds, boolean asAsciiArmoredStringArray,
- IKeychainGetKeyringsHandler handler) throws RemoteException {
- getSecretKeyRingsSafe(masterKeyIds, asAsciiArmoredStringArray, handler);
- }
-
- };
-
- /**
- * As we can not throw an exception through Android RPC, we assign identifiers to the exception
- * types.
- *
- * @param e
- * @return
- */
- // private int getExceptionId(Exception e) {
- // if (e instanceof NoSuchProviderException) {
- // return 0;
- // } else if (e instanceof NoSuchAlgorithmException) {
- // return 1;
- // } else if (e instanceof SignatureException) {
- // return 2;
- // } else if (e instanceof IOException) {
- // return 3;
- // } else if (e instanceof ApgGeneralException) {
- // return 4;
- // } else if (e instanceof PGPException) {
- // return 5;
- // } else {
- // return -1;
- // }
- // }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java
new file mode 100644
index 000000000..beb31d081
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service.remote;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+
+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.util.Log;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class ExtendedApiService extends RemoteService {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ private void selfSignedX509CertSafe(String subjAltNameURI, IExtendedApiCallback callback,
+ AppSettings appSettings) throws RemoteException {
+
+ // TODO: for pgp keyrings with password
+ CallbackHandler pgpPwdCallbackHandler = new PgpToX509.PredefinedPasswordCallbackHandler("");
+
+ try {
+ long keyId = appSettings.getKeyId();
+ PGPSecretKey pgpSecretKey = PgpHelper.getSigningKey(this, keyId);
+
+ PasswordCallback pgpSecKeyPasswordCallBack = new PasswordCallback("pgp passphrase?",
+ false);
+ pgpPwdCallbackHandler.handle(new Callback[] { pgpSecKeyPasswordCallBack });
+ PGPPrivateKey pgpPrivKey = pgpSecretKey.extractPrivateKey(
+ pgpSecKeyPasswordCallBack.getPassword(), PgpMain.BOUNCY_CASTLE_PROVIDER_NAME);
+ pgpSecKeyPasswordCallBack.clearPassword();
+
+ X509Certificate selfSignedCert = PgpToX509.createSelfSignedCert(pgpSecretKey,
+ pgpPrivKey, subjAltNameURI);
+
+ // Write x509cert and privKey into files
+ // FileOutputStream fosCert = context.openFileOutput(CERT_FILENAME,
+ // Context.MODE_PRIVATE);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ PEMWriter pemWriterCert = new PEMWriter(new PrintWriter(outStream));
+ pemWriterCert.writeObject(selfSignedCert);
+ pemWriterCert.close();
+
+ byte[] outputBytes = outStream.toByteArray();
+
+ callback.onSuccess(outputBytes);
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "ExtendedApiService", e);
+ callback.onError(e.getMessage());
+ }
+
+ // TODO: no private key at the moment! Don't give it to others
+ // PrivateKey privKey = pgpPrivKey.getKey();
+ // FileOutputStream fosKey = context.openFileOutput(PRIV_KEY_FILENAME,
+ // Context.MODE_PRIVATE);
+ // PEMWriter pemWriterKey = new PEMWriter(new PrintWriter(fosKey));
+ // pemWriterKey.writeObject(privKey);
+ // pemWriterKey.close();
+ }
+
+ private final IExtendedApiService.Stub mBinder = new IExtendedApiService.Stub() {
+
+ @Override
+ public void encrypt(byte[] inputBytes, String passphrase, IExtendedApiCallback callback)
+ throws RemoteException {
+ // TODO : implement
+
+ }
+
+ @Override
+ public void selfSignedX509Cert(final String subjAltNameURI,
+ final IExtendedApiCallback callback) throws RemoteException {
+ final AppSettings settings = getAppSettings();
+
+ Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ selfSignedX509CertSafe(subjAltNameURI, callback, settings);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "OpenPgpService", e);
+ }
+ }
+ };
+
+ checkAndEnqueue(r);
+
+ }
+
+ };
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl
index c3a7d1faf..f69f66fd7 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,16 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package org.sufficientlysecure.keychain.service.handler;
-interface IKeychainGetKeyringsHandler {
- /**
- * Either outputBytes or outputString is given. One of them is null
- *
- */
- oneway void onSuccess(in byte[] outputBytes, in List<String> outputString);
+package org.sufficientlysecure.keychain.service.remote;
+interface IExtendedApiCallback {
+
+ oneway void onSuccess(in byte[] outputBytes);
- oneway void onException(in int exceptionNumber, in String message);
+ oneway void onError(in String error);
} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl
new file mode 100644
index 000000000..669bd31b5
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * 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.service.remote;
+
+import org.sufficientlysecure.keychain.service.remote.IExtendedApiCallback;
+
+/**
+ * All methods are oneway, which means they are asynchronous and non-blocking.
+ * Results are returned to the callback, which has to be implemented on client side.
+ */
+interface IExtendedApiService {
+
+ /**
+ * Symmetric Encrypt
+ *
+ * @param inputBytes
+ * Byte array you want to encrypt
+ * @param passphrase
+ * symmetric passhprase
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void encrypt(in byte[] inputBytes, in String passphrase, in IExtendedApiCallback callback);
+
+ /**
+ * Generates self signed X509 certificate signed by OpenPGP private key (from app settings)
+ *
+ * @param subjAltNameURI
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void selfSignedX509Cert(in String subjAltNameURI, in IExtendedApiCallback callback);
+
+} \ No newline at end of file
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 7955a7f48..ed2c12419 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
@@ -49,18 +49,18 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-public class OpenPgpService extends RemoteApiService {
+public class OpenPgpService extends RemoteService {
@Override
public void onCreate() {
super.onCreate();
- Log.d(Constants.TAG, "CryptoService, onCreate()");
+ Log.d(Constants.TAG, "OpenPgpService, onCreate()");
}
@Override
public void onDestroy() {
super.onDestroy();
- Log.d(Constants.TAG, "CryptoService, onDestroy()");
+ Log.d(Constants.TAG, "OpenPgpService, onDestroy()");
}
@Override
@@ -69,26 +69,26 @@ public class OpenPgpService extends RemoteApiService {
}
private String getCachedPassphrase(long keyId) {
- String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
+ String passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
if (passphrase == null) {
Log.d(Constants.TAG, "No passphrase! Activity required!");
// start passphrase dialog
Bundle extras = new Bundle();
- extras.putLong(OpenPgpServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
+ extras.putLong(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
PassphraseActivityCallback callback = new PassphraseActivityCallback();
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
- pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_CACHE_PASSPHRASE,
+ pauseQueueAndStartServiceActivity(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE,
messenger, extras);
if (callback.isSuccess()) {
Log.d(Constants.TAG, "New passphrase entered!");
// get again after it was entered
- passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
} else {
Log.d(Constants.TAG, "Passphrase dialog canceled!");
@@ -165,12 +165,12 @@ public class OpenPgpService extends RemoteApiService {
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
Bundle extras = new Bundle();
- extras.putLongArray(OpenPgpServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
- extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
- extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_DUBLICATE_USER_IDS,
+ extras.putLongArray(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
+ extras.putStringArrayList(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
+ extras.putStringArrayList(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS,
dublicateUserIds);
- pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_SELECT_PUB_KEYS,
+ pauseQueueAndStartServiceActivity(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS,
messenger, extras);
if (callback.isSuccess()) {
@@ -238,12 +238,12 @@ public class OpenPgpService extends RemoteApiService {
return;
}
- PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor,
+ PgpMain.encryptAndSign(getContext(), null, inputData, outputStream, asciiArmor,
appSettings.getCompression(), keyIds, null,
appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
appSettings.getHashAlgorithm(), true, passphrase);
} else {
- PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor,
+ PgpMain.encryptAndSign(getContext(), null, inputData, outputStream, asciiArmor,
appSettings.getCompression(), keyIds, null,
appSettings.getEncryptionAlgorithm(), Id.key.none,
appSettings.getHashAlgorithm(), true, null);
@@ -345,7 +345,7 @@ public class OpenPgpService extends RemoteApiService {
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
// app, Fix this?
- // long secretKeyId = PgpMain.getDecryptionKeyId(mContext, inputStream);
+ // long secretKeyId = PgpMain.getDecryptionKeyId(getContext(), inputStream);
// if (secretKeyId == Id.key.none) {
// throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound));
// }
@@ -363,11 +363,10 @@ public class OpenPgpService extends RemoteApiService {
long secretKeyId;
try {
if (inputStream2.markSupported()) {
- inputStream2.mark(200); // should probably set this to the max size of two
- // pgpF
- // objects, if it even needs to be anything other
- // than
- // 0.
+ // should probably set this to the max size of two
+ // pgpF objects, if it even needs to be anything other
+ // than 0.
+ inputStream2.mark(200);
}
secretKeyId = PgpMain.getDecryptionKeyId(this, inputStream2);
if (secretKeyId == Id.key.none) {
@@ -471,7 +470,7 @@ public class OpenPgpService extends RemoteApiService {
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
settings, false);
} catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoService", e);
+ Log.e(Constants.TAG, "OpenPgpService", e);
}
}
};
@@ -493,7 +492,7 @@ public class OpenPgpService extends RemoteApiService {
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
settings, true);
} catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoService", e);
+ Log.e(Constants.TAG, "OpenPgpService", e);
}
}
};
@@ -513,7 +512,7 @@ public class OpenPgpService extends RemoteApiService {
try {
signSafe(inputBytes, callback, settings);
} catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoService", e);
+ Log.e(Constants.TAG, "OpenPgpService", e);
}
}
};
@@ -535,7 +534,7 @@ public class OpenPgpService extends RemoteApiService {
try {
decryptAndVerifySafe(inputBytes, callback, settings);
} catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoService", e);
+ Log.e(Constants.TAG, "OpenPgpService", e);
}
}
};
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteApiService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java
index 898aad90e..1db9b5f66 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteApiService.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java
@@ -40,12 +40,12 @@ import android.os.Messenger;
/**
* Abstract service for remote APIs that handle app registration and user input.
*/
-public abstract class RemoteApiService extends Service {
+public abstract class RemoteService extends Service {
Context mContext;
- final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
+ private final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
// TODO: Are these parameters okay?
- PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
+ private PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
TimeUnit.SECONDS, mPoolQueue);
private final Object userInputLock = new Object();
@@ -87,6 +87,10 @@ public abstract class RemoteApiService extends Service {
}
+ public Context getContext() {
+ return mContext;
+ }
+
/**
* Should be used from Stub implementations of AIDL interfaces to enqueue a runnable for
* execution
@@ -105,12 +109,12 @@ public abstract class RemoteApiService extends Service {
Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!");
Bundle extras = new Bundle();
// TODO: currently simply uses first entry
- extras.putString(OpenPgpServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]);
+ extras.putString(RemoteServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]);
RegisterActivityCallback callback = new RegisterActivityCallback();
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
- pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_REGISTER, messenger,
+ pauseQueueAndStartServiceActivity(RemoteServiceActivity.ACTION_REGISTER, messenger,
extras);
if (callback.isAllowed()) {
@@ -135,11 +139,11 @@ public abstract class RemoteApiService extends Service {
mThreadPool.pause();
Log.d(Constants.TAG, "starting activity...");
- Intent intent = new Intent(getBaseContext(), OpenPgpServiceActivity.class);
+ Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(action);
- extras.putParcelable(OpenPgpServiceActivity.EXTRA_MESSENGER, messenger);
+ extras.putParcelable(RemoteServiceActivity.EXTRA_MESSENGER, messenger);
intent.putExtras(extras);
startActivity(intent);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
index 5b055f6ab..c5bb1e4bd 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/OpenPgpServiceActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
@@ -41,7 +41,7 @@ import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
-public class OpenPgpServiceActivity extends SherlockFragmentActivity {
+public class RemoteServiceActivity extends SherlockFragmentActivity {
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
@@ -84,7 +84,7 @@ public class OpenPgpServiceActivity extends SherlockFragmentActivity {
if (!finishHandled) {
Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL;
+ msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
@@ -120,17 +120,17 @@ public class OpenPgpServiceActivity extends SherlockFragmentActivity {
// user needs to select a key!
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
- Toast.makeText(OpenPgpServiceActivity.this,
+ Toast.makeText(RemoteServiceActivity.this,
R.string.api_register_error_select_key, Toast.LENGTH_LONG)
.show();
} else {
- ProviderHelper.insertApiApp(OpenPgpServiceActivity.this,
+ ProviderHelper.insertApiApp(RemoteServiceActivity.this,
mSettingsFragment.getAppSettings());
Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.RegisterActivityCallback.OKAY;
+ msg.arg1 = RemoteService.RegisterActivityCallback.OKAY;
Bundle data = new Bundle();
- data.putString(OpenPgpService.RegisterActivityCallback.PACKAGE_NAME,
+ data.putString(RemoteService.RegisterActivityCallback.PACKAGE_NAME,
packageName);
msg.setData(data);
try {
@@ -149,7 +149,7 @@ public class OpenPgpServiceActivity extends SherlockFragmentActivity {
// Disallow
Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL;
+ msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {