From bbba67dec245b7799feb8347c346a87e01e55402 Mon Sep 17 00:00:00 2001 From: Daniele Ricci Date: Tue, 8 Jul 2014 16:45:44 +0200 Subject: [PATCH] Export keys in PEM/armored and as PKCS#12 pack (closes #93) Signed-off-by: Daniele Ricci --- src/org/kontalk/Kontalk.java | 5 ++- .../kontalk/authenticator/Authenticator.java | 37 ++++++++++++++----- src/org/kontalk/crypto/X509Bridge.java | 3 ++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/org/kontalk/Kontalk.java b/src/org/kontalk/Kontalk.java index 4ba7b7e37..559c60e11 100644 --- a/src/org/kontalk/Kontalk.java +++ b/src/org/kontalk/Kontalk.java @@ -19,6 +19,8 @@ package org.kontalk; import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; @@ -188,7 +190,8 @@ public PersonalKey getPersonalKey() throws PGPException, IOException, Certificat } public void exportPersonalKey() - throws CertificateException, PGPException, IOException, NoSuchProviderException { + throws CertificateException, PGPException, IOException, + NoSuchProviderException, KeyStoreException, NoSuchAlgorithmException { Authenticator.exportDefaultPersonalKey(this, getCachedPassphrase(), true); } diff --git a/src/org/kontalk/authenticator/Authenticator.java b/src/org/kontalk/authenticator/Authenticator.java index 920b75755..dc66c1521 100644 --- a/src/org/kontalk/authenticator/Authenticator.java +++ b/src/org/kontalk/authenticator/Authenticator.java @@ -20,17 +20,27 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import org.kontalk.R; import org.kontalk.crypto.PGP; import org.kontalk.crypto.PersonalKey; +import org.kontalk.crypto.X509Bridge; import org.kontalk.ui.NumberValidation; import org.kontalk.util.MessageUtils; +import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.openpgp.PGPException; +import org.spongycastle.util.io.pem.PemObject; +import org.spongycastle.util.io.pem.PemWriter; import android.accounts.AbstractAccountAuthenticator; import android.accounts.Account; @@ -70,6 +80,7 @@ public class Authenticator extends AbstractAccountAuthenticator { public static final String PRIVATE_KEY_FILENAME = "kontalk-private.pgp"; public static final String BRIDGE_CERT_FILENAME = "kontalk-login.crt"; public static final String BRIDGE_KEY_FILENAME = "kontalk-login.key"; + public static final String BRIDGE_CERTPACK_FILENAME = "kontalk-login.p12"; private final Context mContext; private final Handler mHandler; @@ -127,7 +138,8 @@ public static PersonalKey loadDefaultPersonalKey(Context ctx, String passphrase) } public static void exportDefaultPersonalKey(Context ctx, String passphrase, boolean bridgeCertificate) - throws CertificateException, NoSuchProviderException, PGPException, IOException { + throws CertificateException, NoSuchProviderException, PGPException, + IOException, KeyStoreException, NoSuchAlgorithmException { AccountManager m = AccountManager.get(ctx); Account acc = getDefaultAccount(m); @@ -136,7 +148,7 @@ public static void exportDefaultPersonalKey(Context ctx, String passphrase, bool String pubKeyData = m.getUserData(acc, DATA_PUBLICKEY); File path = Environment.getExternalStorageDirectory(); - FileOutputStream out; + OutputStream out; byte[] publicKey = Base64.decode(pubKeyData, Base64.DEFAULT); byte[] privateKey = Base64.decode(privKeyData, Base64.DEFAULT); @@ -147,24 +159,31 @@ public static void exportDefaultPersonalKey(Context ctx, String passphrase, bool byte[] bridgeCert = Base64.decode(bridgeCertData, Base64.DEFAULT); // export bridge certificate - out = new FileOutputStream(new File(path, BRIDGE_CERT_FILENAME)); - out.write(bridgeCert); - out.close(); + PemWriter writer = new PemWriter(new FileWriter(new File(path, BRIDGE_CERT_FILENAME))); + writer.writeObject(new PemObject(X509Bridge.PEM_TYPE_CERTIFICATE, bridgeCert)); + writer.close(); // export bridge private key PrivateKey bridgeKey = PGP.convertPrivateKey(privateKey, passphrase); - out = new FileOutputStream(new File(path, BRIDGE_KEY_FILENAME)); - out.write(bridgeKey.getEncoded()); + writer = new PemWriter(new FileWriter(new File(path, BRIDGE_KEY_FILENAME))); + writer.writeObject(new PemObject(X509Bridge.PEM_TYPE_PRIVATE_KEY, bridgeKey.getEncoded())); + writer.close(); + + // certificate pack in PKCS#12 + X509Certificate certificate = X509Bridge.load(bridgeCert); + KeyStore pkcs12 = X509Bridge.exportCertificate(certificate, bridgeKey); + out = new FileOutputStream(new File(path, BRIDGE_CERTPACK_FILENAME)); + pkcs12.store(out, passphrase.toCharArray()); out.close(); } // export public key - out = new FileOutputStream(new File(path, PUBLIC_KEY_FILENAME)); + out = new ArmoredOutputStream(new FileOutputStream(new File(path, PUBLIC_KEY_FILENAME))); out.write(publicKey); out.close(); // export private key - out = new FileOutputStream(new File(path, PRIVATE_KEY_FILENAME)); + out = new ArmoredOutputStream(new FileOutputStream(new File(path, PRIVATE_KEY_FILENAME))); out.write(privateKey); out.close(); } diff --git a/src/org/kontalk/crypto/X509Bridge.java b/src/org/kontalk/crypto/X509Bridge.java index 30bdcd0ad..be0abfe0d 100644 --- a/src/org/kontalk/crypto/X509Bridge.java +++ b/src/org/kontalk/crypto/X509Bridge.java @@ -77,6 +77,9 @@ */ public class X509Bridge { + public static final String PEM_TYPE_PRIVATE_KEY = "RSA PRIVATE KEY"; + public static final String PEM_TYPE_CERTIFICATE = "CERTIFICATE"; + private final static String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge"; private X509Bridge() {