mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-30 12:32:17 -05:00
Extended api
This commit is contained in:
parent
1e188ee2fa
commit
312b735fbd
@ -358,29 +358,6 @@
|
|||||||
android:name="org.sufficientlysecure.keychain.service.KeychainIntentService"
|
android:name="org.sufficientlysecure.keychain.service.KeychainIntentService"
|
||||||
android:exported="false" />
|
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
|
<provider
|
||||||
android:name="org.sufficientlysecure.keychain.provider.KeychainProvider"
|
android:name="org.sufficientlysecure.keychain.provider.KeychainProvider"
|
||||||
android:authorities="org.sufficientlysecure.keychain.provider"
|
android:authorities="org.sufficientlysecure.keychain.provider"
|
||||||
@ -393,15 +370,15 @@
|
|||||||
<!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
|
<!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- OpenPGP API internal classes (not exported) -->
|
<!-- OpenPGP Remote API internal classes (not exported) -->
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpServiceActivity"
|
android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:process=":openpgp_api"
|
android:process=":remote_api"
|
||||||
android:taskAffinity=":openpgp_api" />
|
android:taskAffinity=":remote_api" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
|
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
@ -412,13 +389,13 @@
|
|||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- OpenPGP API -->
|
<!-- OpenPGP Remote API -->
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpService"
|
android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:process=":openpgp_api" >
|
android:process=":remote_api" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.openintents.openpgp.IOpenPgpService" />
|
<action android:name="org.openintents.openpgp.IOpenPgpService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -427,6 +404,22 @@
|
|||||||
android:name="api_version"
|
android:name="api_version"
|
||||||
android:value="1" />
|
android:value="1" />
|
||||||
</service>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
BIN
OpenPGP-Keychain/libs/scpkix-jdk15on-1.47.0.2.jar
Normal file
BIN
OpenPGP-Keychain/libs/scpkix-jdk15on-1.47.0.2.jar
Normal file
Binary file not shown.
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,15 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.handler;
|
package org.sufficientlysecure.keychain.service.remote;
|
||||||
|
|
||||||
interface IKeychainGetKeyringsHandler {
|
interface IExtendedApiCallback {
|
||||||
/**
|
|
||||||
* Either outputBytes or outputString is given. One of them is null
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
oneway void onSuccess(in byte[] outputBytes, in List<String> outputString);
|
|
||||||
|
|
||||||
|
oneway void onSuccess(in byte[] outputBytes);
|
||||||
|
|
||||||
oneway void onException(in int exceptionNumber, in String message);
|
oneway void onError(in String error);
|
||||||
}
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -49,18 +49,18 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
public class OpenPgpService extends RemoteApiService {
|
public class OpenPgpService extends RemoteService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
Log.d(Constants.TAG, "CryptoService, onCreate()");
|
Log.d(Constants.TAG, "OpenPgpService, onCreate()");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
Log.d(Constants.TAG, "CryptoService, onDestroy()");
|
Log.d(Constants.TAG, "OpenPgpService, onDestroy()");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -69,26 +69,26 @@ public class OpenPgpService extends RemoteApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getCachedPassphrase(long keyId) {
|
private String getCachedPassphrase(long keyId) {
|
||||||
String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
|
String passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
|
||||||
|
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
Log.d(Constants.TAG, "No passphrase! Activity required!");
|
Log.d(Constants.TAG, "No passphrase! Activity required!");
|
||||||
|
|
||||||
// start passphrase dialog
|
// start passphrase dialog
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
extras.putLong(OpenPgpServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
extras.putLong(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
||||||
|
|
||||||
PassphraseActivityCallback callback = new PassphraseActivityCallback();
|
PassphraseActivityCallback callback = new PassphraseActivityCallback();
|
||||||
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
||||||
|
|
||||||
pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_CACHE_PASSPHRASE,
|
pauseQueueAndStartServiceActivity(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE,
|
||||||
messenger, extras);
|
messenger, extras);
|
||||||
|
|
||||||
if (callback.isSuccess()) {
|
if (callback.isSuccess()) {
|
||||||
Log.d(Constants.TAG, "New passphrase entered!");
|
Log.d(Constants.TAG, "New passphrase entered!");
|
||||||
|
|
||||||
// get again after it was entered
|
// get again after it was entered
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
|
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Constants.TAG, "Passphrase dialog canceled!");
|
Log.d(Constants.TAG, "Passphrase dialog canceled!");
|
||||||
|
|
||||||
@ -165,12 +165,12 @@ public class OpenPgpService extends RemoteApiService {
|
|||||||
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
||||||
|
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
extras.putLongArray(OpenPgpServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
extras.putLongArray(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
||||||
extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
extras.putStringArrayList(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
||||||
extras.putStringArrayList(OpenPgpServiceActivity.EXTRA_DUBLICATE_USER_IDS,
|
extras.putStringArrayList(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS,
|
||||||
dublicateUserIds);
|
dublicateUserIds);
|
||||||
|
|
||||||
pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_SELECT_PUB_KEYS,
|
pauseQueueAndStartServiceActivity(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS,
|
||||||
messenger, extras);
|
messenger, extras);
|
||||||
|
|
||||||
if (callback.isSuccess()) {
|
if (callback.isSuccess()) {
|
||||||
@ -238,12 +238,12 @@ public class OpenPgpService extends RemoteApiService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor,
|
PgpMain.encryptAndSign(getContext(), null, inputData, outputStream, asciiArmor,
|
||||||
appSettings.getCompression(), keyIds, null,
|
appSettings.getCompression(), keyIds, null,
|
||||||
appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
|
appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
|
||||||
appSettings.getHashAlgorithm(), true, passphrase);
|
appSettings.getHashAlgorithm(), true, passphrase);
|
||||||
} else {
|
} else {
|
||||||
PgpMain.encryptAndSign(mContext, null, inputData, outputStream, asciiArmor,
|
PgpMain.encryptAndSign(getContext(), null, inputData, outputStream, asciiArmor,
|
||||||
appSettings.getCompression(), keyIds, null,
|
appSettings.getCompression(), keyIds, null,
|
||||||
appSettings.getEncryptionAlgorithm(), Id.key.none,
|
appSettings.getEncryptionAlgorithm(), Id.key.none,
|
||||||
appSettings.getHashAlgorithm(), true, null);
|
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
|
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
|
||||||
// app, Fix this?
|
// app, Fix this?
|
||||||
// long secretKeyId = PgpMain.getDecryptionKeyId(mContext, inputStream);
|
// long secretKeyId = PgpMain.getDecryptionKeyId(getContext(), inputStream);
|
||||||
// if (secretKeyId == Id.key.none) {
|
// if (secretKeyId == Id.key.none) {
|
||||||
// throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound));
|
// throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound));
|
||||||
// }
|
// }
|
||||||
@ -363,11 +363,10 @@ public class OpenPgpService extends RemoteApiService {
|
|||||||
long secretKeyId;
|
long secretKeyId;
|
||||||
try {
|
try {
|
||||||
if (inputStream2.markSupported()) {
|
if (inputStream2.markSupported()) {
|
||||||
inputStream2.mark(200); // should probably set this to the max size of two
|
// should probably set this to the max size of two
|
||||||
// pgpF
|
// pgpF objects, if it even needs to be anything other
|
||||||
// objects, if it even needs to be anything other
|
// than 0.
|
||||||
// than
|
inputStream2.mark(200);
|
||||||
// 0.
|
|
||||||
}
|
}
|
||||||
secretKeyId = PgpMain.getDecryptionKeyId(this, inputStream2);
|
secretKeyId = PgpMain.getDecryptionKeyId(this, inputStream2);
|
||||||
if (secretKeyId == Id.key.none) {
|
if (secretKeyId == Id.key.none) {
|
||||||
@ -471,7 +470,7 @@ public class OpenPgpService extends RemoteApiService {
|
|||||||
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
|
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
|
||||||
settings, false);
|
settings, false);
|
||||||
} catch (RemoteException e) {
|
} 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,
|
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, callback,
|
||||||
settings, true);
|
settings, true);
|
||||||
} catch (RemoteException e) {
|
} 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 {
|
try {
|
||||||
signSafe(inputBytes, callback, settings);
|
signSafe(inputBytes, callback, settings);
|
||||||
} catch (RemoteException e) {
|
} 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 {
|
try {
|
||||||
decryptAndVerifySafe(inputBytes, callback, settings);
|
decryptAndVerifySafe(inputBytes, callback, settings);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(Constants.TAG, "CryptoService", e);
|
Log.e(Constants.TAG, "OpenPgpService", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -40,12 +40,12 @@ import android.os.Messenger;
|
|||||||
/**
|
/**
|
||||||
* Abstract service for remote APIs that handle app registration and user input.
|
* 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;
|
Context mContext;
|
||||||
|
|
||||||
final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
|
private final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
|
||||||
// TODO: Are these parameters okay?
|
// TODO: Are these parameters okay?
|
||||||
PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
|
private PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
|
||||||
TimeUnit.SECONDS, mPoolQueue);
|
TimeUnit.SECONDS, mPoolQueue);
|
||||||
|
|
||||||
private final Object userInputLock = new Object();
|
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
|
* Should be used from Stub implementations of AIDL interfaces to enqueue a runnable for
|
||||||
* execution
|
* execution
|
||||||
@ -105,12 +109,12 @@ public abstract class RemoteApiService extends Service {
|
|||||||
Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!");
|
Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!");
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
// TODO: currently simply uses first entry
|
// 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();
|
RegisterActivityCallback callback = new RegisterActivityCallback();
|
||||||
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
||||||
|
|
||||||
pauseQueueAndStartServiceActivity(OpenPgpServiceActivity.ACTION_REGISTER, messenger,
|
pauseQueueAndStartServiceActivity(RemoteServiceActivity.ACTION_REGISTER, messenger,
|
||||||
extras);
|
extras);
|
||||||
|
|
||||||
if (callback.isAllowed()) {
|
if (callback.isAllowed()) {
|
||||||
@ -135,11 +139,11 @@ public abstract class RemoteApiService extends Service {
|
|||||||
mThreadPool.pause();
|
mThreadPool.pause();
|
||||||
|
|
||||||
Log.d(Constants.TAG, "starting activity...");
|
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.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
intent.setAction(action);
|
intent.setAction(action);
|
||||||
|
|
||||||
extras.putParcelable(OpenPgpServiceActivity.EXTRA_MESSENGER, messenger);
|
extras.putParcelable(RemoteServiceActivity.EXTRA_MESSENGER, messenger);
|
||||||
intent.putExtras(extras);
|
intent.putExtras(extras);
|
||||||
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
@ -41,7 +41,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
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_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
|
||||||
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
|
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
|
||||||
@ -84,7 +84,7 @@ public class OpenPgpServiceActivity extends SherlockFragmentActivity {
|
|||||||
|
|
||||||
if (!finishHandled) {
|
if (!finishHandled) {
|
||||||
Message msg = Message.obtain();
|
Message msg = Message.obtain();
|
||||||
msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL;
|
msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
|
||||||
try {
|
try {
|
||||||
mMessenger.send(msg);
|
mMessenger.send(msg);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@ -120,17 +120,17 @@ public class OpenPgpServiceActivity extends SherlockFragmentActivity {
|
|||||||
|
|
||||||
// user needs to select a key!
|
// user needs to select a key!
|
||||||
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
|
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)
|
R.string.api_register_error_select_key, Toast.LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
ProviderHelper.insertApiApp(OpenPgpServiceActivity.this,
|
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
|
||||||
mSettingsFragment.getAppSettings());
|
mSettingsFragment.getAppSettings());
|
||||||
|
|
||||||
Message msg = Message.obtain();
|
Message msg = Message.obtain();
|
||||||
msg.arg1 = OpenPgpService.RegisterActivityCallback.OKAY;
|
msg.arg1 = RemoteService.RegisterActivityCallback.OKAY;
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putString(OpenPgpService.RegisterActivityCallback.PACKAGE_NAME,
|
data.putString(RemoteService.RegisterActivityCallback.PACKAGE_NAME,
|
||||||
packageName);
|
packageName);
|
||||||
msg.setData(data);
|
msg.setData(data);
|
||||||
try {
|
try {
|
||||||
@ -149,7 +149,7 @@ public class OpenPgpServiceActivity extends SherlockFragmentActivity {
|
|||||||
// Disallow
|
// Disallow
|
||||||
|
|
||||||
Message msg = Message.obtain();
|
Message msg = Message.obtain();
|
||||||
msg.arg1 = OpenPgpService.RegisterActivityCallback.CANCEL;
|
msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
|
||||||
try {
|
try {
|
||||||
mMessenger.send(msg);
|
mMessenger.send(msg);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
@ -64,7 +64,7 @@ OpenPGP Keychain specific Intent actions:
|
|||||||
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE``
|
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE``
|
||||||
* without extras starts Barcode Scanner to get QR Code
|
* without extras starts Barcode Scanner to get QR Code
|
||||||
|
|
||||||
## Remote Service API
|
## OpenPGP Remote API
|
||||||
To do asyncronous fast encryption/decryption/sign/verify operations bind to the remote service.
|
To do asyncronous fast encryption/decryption/sign/verify operations bind to the remote service.
|
||||||
The API Demo contains all required AIDL files and a demo activity.
|
The API Demo contains all required AIDL files and a demo activity.
|
||||||
|
|
||||||
@ -118,6 +118,10 @@ for integration.
|
|||||||
|
|
||||||
[4] https://play.google.com/stor/apps/details?id=org.sufficientlysecure.keychain.demo
|
[4] https://play.google.com/stor/apps/details?id=org.sufficientlysecure.keychain.demo
|
||||||
|
|
||||||
|
## Extended Remote API
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
# Libraries
|
# Libraries
|
||||||
|
|
||||||
All JAR-Libraries are provided in this repository under "libs", all Android Library projects are under "libraries".
|
All JAR-Libraries are provided in this repository under "libs", all Android Library projects are under "libraries".
|
||||||
|
Loading…
Reference in New Issue
Block a user