package org.witness.informacam.crypto; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.security.SignatureException; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.BCPGOutputStream; import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.openpgp.PGPCompressedData; import org.spongycastle.openpgp.PGPCompressedDataGenerator; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRingCollection; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRingCollection; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.util.encoders.Hex; import org.witness.informacam.InformaCam; import org.witness.informacam.json.JSONArray; import org.witness.informacam.json.JSONException; import org.witness.informacam.json.JSONObject; import org.witness.informacam.json.JSONTokener; import org.witness.informacam.models.credentials.IKeyStore; import org.witness.informacam.models.credentials.ISecretKey; import org.witness.informacam.models.notifications.INotification; import org.witness.informacam.models.organizations.IOrganization; import org.witness.informacam.storage.FormUtility; import org.witness.informacam.storage.IOUtility; import org.witness.informacam.utils.Constants.App; import org.witness.informacam.utils.Constants.App.Storage; import org.witness.informacam.utils.Constants.App.Storage.Type; import org.witness.informacam.utils.Constants.Codes; import org.witness.informacam.utils.Constants.IManifest; import org.witness.informacam.utils.Constants.Models; import org.witness.informacam.utils.Constants.Models.ICredentials; import org.witness.informacam.utils.Constants.Models.IUser; import android.os.Bundle; import android.os.Message; import android.util.Base64; import android.util.Log; public class KeyUtility { private final static String LOG = App.Crypto.LOG; public static String getFingerprintFromKey(byte[] keyblock) throws IOException, PGPException { PGPPublicKey key = extractPublicKeyFromBytes(keyblock); return new String(Hex.encode(key.getFingerprint())); } @SuppressWarnings("unchecked") public static PGPSecretKey extractSecretKey(byte[] keyblock) { PGPSecretKey secretKey = null; try { PGPSecretKeyRingCollection pkrc = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(Base64.decode(keyblock, Base64.DEFAULT)))); Iterator<PGPSecretKeyRing> rIt = pkrc.getKeyRings(); while(rIt.hasNext()) { PGPSecretKeyRing pkr = (PGPSecretKeyRing) rIt.next(); Iterator<PGPSecretKey> kIt = pkr.getSecretKeys(); while(secretKey == null && kIt.hasNext()) { secretKey = kIt.next(); } } return secretKey; } catch(IOException e) { return null; } catch(PGPException e) { return null; } } @SuppressWarnings("unchecked") public static PGPPublicKey extractPublicKeyFromBytes(byte[] keyBlock) throws IOException, PGPException { PGPPublicKeyRingCollection keyringCol = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(Base64.decode(keyBlock, Base64.DEFAULT)))); PGPPublicKey key = null; Iterator<PGPPublicKeyRing> rIt = keyringCol.getKeyRings(); while(key == null && rIt.hasNext()) { PGPPublicKeyRing keyring = (PGPPublicKeyRing) rIt.next(); Iterator<PGPPublicKey> kIt = keyring.getPublicKeys(); while(key == null && kIt.hasNext()) { PGPPublicKey k = (PGPPublicKey) kIt.next(); if(k.isEncryptionKey()) key = k; } } if(key == null) { throw new IllegalArgumentException("there isn't an encryption key here."); } return key; } public static String generatePassword(byte[] baseBytes) throws NoSuchAlgorithmException { // initialize random bytes byte[] randomBytes = new byte[baseBytes.length]; SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); sr.nextBytes(randomBytes); // xor by baseImage byte[] product = new byte[baseBytes.length]; for(int b = 0; b < baseBytes.length; b++) { product[b] = (byte) (baseBytes[b] ^ randomBytes[b]); } // digest to SHA1 string, voila password. MessageDigest md = MessageDigest.getInstance("SHA-1"); return Base64.encodeToString(md.digest(product), Base64.DEFAULT); } @SuppressWarnings("deprecation") public static boolean initDevice() { int progress = 1; Bundle data = new Bundle(); data.putInt(Codes.Extras.MESSAGE_CODE, Codes.Messages.UI.UPDATE); data.putInt(Codes.Keys.UI.PROGRESS, progress); InformaCam informaCam = InformaCam.getInstance(); informaCam.update(data); informaCam.setCredentialManager(new CredentialManager(informaCam, !informaCam.ioService.isMounted(), true, true) { @Override public void onCacheWordUninitialized() { if(firstUse) { Log.d(LOG, "INIT: onCacheWordUninitialized()"); try { setMasterPassword(informaCam.user.getString(IUser.PASSWORD).toCharArray()); } catch (Exception e) { Log.e(LOG, e.toString()); e.printStackTrace(); } } else { super.onCacheWordUninitialized(); } } @Override public void onCacheWordOpened() { // there is not credential block, so override this. if(firstUse) { Log.d(LOG, "INIT: onCacheWordOpened()"); cacheWord.setTimeout(0); informaCam.ioService.initIOCipher(cacheWord.getEncryptionKey()); new Thread () { public void run () { initDeviceAsync (informaCam, informaCam.getCredentialManager()); } }.start(); } else { super.onCacheWordOpened(); } } }); return true; } private static void initDeviceAsync (InformaCam informaCam, CredentialManager credMgr) { try { final String authToken; String basePath = informaCam.user.getJSONArray(IUser.PATH_TO_BASE_IMAGE).getString(0); byte[] baseImageBytes = informaCam.ioService.getBytes(basePath, Storage.Type.INTERNAL_STORAGE); authToken = generatePassword(baseImageBytes); String authTokenBlobBytes = new String(credMgr.setAuthToken(authToken)); JSONObject authTokenBlob = (JSONObject) new JSONTokener(authTokenBlobBytes).nextValue(); authTokenBlob.put(ICredentials.PASSWORD_BLOCK, authTokenBlob.getString("value")); authTokenBlob.remove("value"); initDeviceKeys(authToken, baseImageBytes); if(informaCam.ioService.saveBlob(authTokenBlob.toString().getBytes(), new java.io.File(IUser.CREDENTIALS))) { informaCam.user.setHasCredentials(true); } informaCam.initData(); for(String s : informaCam.getAssets().list("includedOrganizations")) { InputStream ictdIS = informaCam.ioService.getStream("includedOrganizations/" + s, Type.APPLICATION_ASSET); byte[] ictdBytes = new byte[ictdIS.available()]; ictdIS.read(ictdBytes); IOrganization organization = informaCam.installICTD((JSONObject) new JSONTokener(new String(ictdBytes)).nextValue(), informaCam.h, informaCam); if(organization != null && !informaCam.user.isInOfflineMode) { /* INotification notification = new INotification(informaCam.getResources().getString(R.string.key_sent), informaCam.getResources().etResources().getString(R.string.you_have_sent_your_credentials_to_x, organization.organizationName), Models.INotification.Type.NEW_KEY); notification.taskComplete = false; informaCam.addNotification(notification, null); */ // ITransportStub transportStub = new ITransportStub(organization, notification); // transportStub.setAsset(IUser.PUBLIC_CREDENTIALS, IUser.PUBLIC_CREDENTIALS, MimeType.ZIP, Type.IOCIPHER); // TransportUtility.initTransport(transportStub); } } try { for(String s : informaCam.getAssets().list("includedForms")) { InputStream formXML = informaCam.ioService.getStream("includedForms/" + s, Type.APPLICATION_ASSET); FormUtility.importAndParse(formXML); } } catch(Exception e) { Log.e(LOG, e.toString()); e.printStackTrace(); } // Tell others we are done! Bundle data = new Bundle(); data.putInt(Codes.Extras.MESSAGE_CODE, org.witness.informacam.utils.Constants.Codes.Messages.UI.REPLACE); Message message = new Message(); message.setData(data); informaCam.update(data); } catch (Exception e) { Log.e(LOG, e.toString(),e); } } private static boolean initDeviceKeys (String authToken, byte[] baseImageBytes) { InformaCam informaCam = InformaCam.getInstance(); int progress = 1; Bundle data = new Bundle(); data.putInt(Codes.Extras.MESSAGE_CODE, Codes.Messages.UI.UPDATE); data.putInt(Codes.Keys.UI.PROGRESS, progress); try { String secretAuthToken, keyStorePassword; progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); secretAuthToken = generatePassword(baseImageBytes); keyStorePassword = generatePassword(baseImageBytes); // TODO: set up anonymization vector here baseImageBytes = null; //informaCam.ioService.initIOCipher(authToken); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); Map<String, InputStream> publicCredentials = new HashMap<String, InputStream>(); JSONArray baseImages = informaCam.user.getJSONArray(IUser.PATH_TO_BASE_IMAGE); for(int j=0; j<baseImages.length(); j++) { InputStream baseImageStream = informaCam.ioService.getStream(baseImages.getString(j), Storage.Type.INTERNAL_STORAGE); info.guardianproject.iocipher.File baseImage = new info.guardianproject.iocipher.File(IUser.BASE_IMAGE + "_" + j); if(informaCam.ioService.saveBlob(baseImageStream, baseImage)) { informaCam.ioService.delete(baseImages.getString(j), Storage.Type.INTERNAL_STORAGE); publicCredentials.put(IUser.BASE_IMAGE + "_" + j, informaCam.ioService.getStream(baseImage.getAbsolutePath(), Storage.Type.IOCIPHER)); } } informaCam.user.remove(IUser.PATH_TO_BASE_IMAGE); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); Security.addProvider(new BouncyCastleProvider()); KeyPairGenerator kpg; kpg = KeyPairGenerator.getInstance("RSA","BC"); kpg.initialize(4096); KeyPair keyPair = kpg.generateKeyPair(); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); PGPSignatureSubpacketGenerator hashedGen = new PGPSignatureSubpacketGenerator(); hashedGen.setKeyFlags(true, KeyFlags.ENCRYPT_STORAGE); hashedGen.setPreferredCompressionAlgorithms(false, new int[] { CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.ZIP }); hashedGen.setPreferredHashAlgorithms(false, new int[] { HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512 }); hashedGen.setPreferredSymmetricAlgorithms(false, new int[] { SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5, SymmetricKeyAlgorithmTags.DES }); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); PGPSecretKey secret = new PGPSecretKey( PGPSignature.DEFAULT_CERTIFICATION, PublicKeyAlgorithmTags.RSA_GENERAL, keyPair.getPublic(), keyPair.getPrivate(), new Date(), "InformaCam OpenPGP Key: " + informaCam.user.getString(IUser.ALIAS), SymmetricKeyAlgorithmTags.AES_256, secretAuthToken.toCharArray(), hashedGen.generate(), null, new SecureRandom(), "BC"); String pgpKeyFingerprint = new String(Hex.encode(secret.getPublicKey().getFingerprint())); informaCam.user.pgpKeyFingerprint = pgpKeyFingerprint; ISecretKey secretKeyPackage = new ISecretKey(); secretKeyPackage.pgpKeyFingerprint = pgpKeyFingerprint; secretKeyPackage.secretAuthToken = secretAuthToken; secretKeyPackage.secretKey = Base64.encodeToString(secret.getEncoded(), Base64.DEFAULT); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ArmoredOutputStream aos = new ArmoredOutputStream(baos); aos.write(secret.getPublicKey().getEncoded()); aos.flush(); aos.close(); baos.flush(); publicCredentials.put(IUser.PUBLIC_KEY, new ByteArrayInputStream(baos.toByteArray())); baos.close(); JSONObject credentials = new JSONObject(); credentials.put(IUser.ALIAS, informaCam.user.getString(IUser.ALIAS)); credentials.put(IUser.EMAIL, informaCam.user.getString(IUser.EMAIL)); publicCredentials.put(IUser.CREDENTIALS, new ByteArrayInputStream(credentials.toString().getBytes())); IOUtility.zipFiles(publicCredentials, IUser.PUBLIC_CREDENTIALS, Type.IOCIPHER); progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); if(informaCam.ioService.saveBlob(new byte[0], new info.guardianproject.iocipher.File(IManifest.KEY_STORE))) { // make keystore manifest IKeyStore keyStoreManifest = new IKeyStore(); keyStoreManifest.password = keyStorePassword; keyStoreManifest.path = IManifest.KEY_STORE; keyStoreManifest.lastModified = System.currentTimeMillis(); informaCam.saveState(keyStoreManifest); Log.d(LOG, "KEY STORE INITED"); } progress += 10; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); if(informaCam.ioService.saveBlob( secretKeyPackage.asJson().toString().getBytes(), new info.guardianproject.iocipher.File(IUser.SECRET)) ) { informaCam.user.alias = informaCam.user.getString(IUser.ALIAS); informaCam.user.email = informaCam.user.getString(IUser.EMAIL); informaCam.user.remove(IUser.AUTH_TOKEN); informaCam.user.remove(IUser.PATH_TO_BASE_IMAGE); informaCam.user.remove(IUser.ALIAS); informaCam.user.remove(IUser.EMAIL); informaCam.user.hasPrivateKey = true; informaCam.user.save(); progress += 9; data.putInt(Codes.Keys.UI.PROGRESS, progress); informaCam.update(data); } return true; } catch (Exception e) { Log.e(LOG, e.toString(),e); } return false; } @SuppressWarnings("deprecation") public static boolean verifySig(byte[] signature, byte[] data, PGPPublicKey publicKey) { BouncyCastleProvider bc = new BouncyCastleProvider(); Security.addProvider(bc); ByteArrayInputStream bais_sig = new ByteArrayInputStream(signature); ByteArrayInputStream bais_data = new ByteArrayInputStream(data); try { InputStream is = PGPUtil.getDecoderStream(bais_sig); PGPObjectFactory objFactory = new PGPObjectFactory(is); PGPCompressedData cData1 = (PGPCompressedData) objFactory.nextObject(); objFactory = new PGPObjectFactory(cData1.getDataStream()); PGPSignatureList sigList = (PGPSignatureList) objFactory.nextObject(); PGPSignature sig = sigList.get(0); sig.initVerify(publicKey, bc); int ch; while((ch = bais_data.read()) >= 0) { sig.update((byte) ch); } if(sig.verify()) { return true; } } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (PGPException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (SignatureException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return false; } @SuppressWarnings({ "deprecation" }) public static byte[] applySignature(byte[] data, PGPSecretKey secretKey, PGPPublicKey publicKey, PGPPrivateKey privateKey) throws NoSuchAlgorithmException, PGPException, IOException, SignatureException { BouncyCastleProvider bc = new BouncyCastleProvider(); Security.addProvider(bc); ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStream targetOut = new ArmoredOutputStream(baos); PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1, bc); sGen.initSign(PGPSignature.BINARY_DOCUMENT, privateKey); PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZLIB); BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(targetOut)); sGen.update(data); sGen.generate().encode(bOut); cGen.close(); bOut.close(); targetOut.close(); byte[] outdata = baos.toByteArray(); return outdata; } @SuppressWarnings({ "deprecation" }) public static void applySignature(InputStream is, OutputStream os, PGPSecretKey secretKey, PGPPublicKey publicKey, PGPPrivateKey privateKey) throws NoSuchAlgorithmException, PGPException, IOException, SignatureException { BouncyCastleProvider bc = new BouncyCastleProvider(); Security.addProvider(bc); OutputStream targetOut = new ArmoredOutputStream(os); PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1, bc); sGen.initSign(PGPSignature.BINARY_DOCUMENT, privateKey); PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZLIB); BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(targetOut)); byte[] buf = new byte[4096]; int len; while ((len = is.read(buf)) > 0) { sGen.update(buf, 0, len); } sGen.generate().encode(bOut); cGen.close(); bOut.close(); targetOut.close(); } }