diff --git a/res/values/strings.xml b/res/values/strings.xml
index 179619796..c5fc6105e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -175,6 +175,7 @@
Deleting current repo…
Adding %s to repo…
Writing raw index file (index.xml)…
+ Writing signed index file (index.jar)…
Linking APKs into the repo…
Copying app icons into the repo…
Finished updating local repo
diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoKeyStore.java b/src/org/fdroid/fdroid/localrepo/LocalRepoKeyStore.java
index 27778c2fb..c81d79a40 100644
--- a/src/org/fdroid/fdroid/localrepo/LocalRepoKeyStore.java
+++ b/src/org/fdroid/fdroid/localrepo/LocalRepoKeyStore.java
@@ -211,30 +211,6 @@ public class LocalRepoKeyStore {
return null;
}
- // This is take from FDroid: org.fdroid.fdroid.DB.calcFingerprint()
- // TODO once this code is part of FDroid, replace this with DB.calcFingerprint()
- public String getFingerprint() {
- String ret = null;
- try {
- Certificate cert = getCertificate();
- if (cert != null) {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- digest.update(cert.getEncoded());
- byte[] fingerprint = digest.digest();
- Formatter formatter = new Formatter(new StringBuilder());
- for (int i = 0; i < fingerprint.length; i++) {
- formatter.format("%02X", fingerprint[i]);
- }
- ret = formatter.toString();
- formatter.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return ret;
- }
-
public Certificate getCertificate() {
try {
Key key = keyStore.getKey(INDEX_CERT_ALIAS, "".toCharArray());
diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java b/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java
index 21108edd1..29b35fc71 100644
--- a/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java
+++ b/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java
@@ -16,9 +16,9 @@ import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
+import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.Utils;
-import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.App;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -28,6 +28,8 @@ import java.security.cert.CertificateEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -42,6 +44,7 @@ public class LocalRepoManager {
// For ref, official F-droid repo presently uses a maxage of 14 days
private static final String DEFAULT_REPO_MAX_AGE_DAYS = "14";
+ private final Context context;
private final PackageManager pm;
private final AssetManager assetManager;
private final SharedPreferences prefs;
@@ -53,12 +56,15 @@ public class LocalRepoManager {
private Map apps = new HashMap();
public final File xmlIndex;
+ private File xmlIndexJar = null;
+ private File xmlIndexJarUnsigned = null;
public final File webRoot;
public final File fdroidDir;
public final File repoDir;
public final File iconsDir;
public LocalRepoManager(Context c) {
+ context = c;
pm = c.getPackageManager();
assetManager = c.getAssets();
prefs = PreferenceManager.getDefaultSharedPreferences(c);
@@ -70,6 +76,8 @@ public class LocalRepoManager {
repoDir = new File(fdroidDir, "repo");
iconsDir = new File(repoDir, "icons");
xmlIndex = new File(repoDir, "index.xml");
+ xmlIndexJar = new File(repoDir, "index.jar");
+ xmlIndexJarUnsigned = new File(repoDir, "index.unsigned.jar");
if (!fdroidDir.exists())
if (!fdroidDir.mkdir())
@@ -278,6 +286,7 @@ public class LocalRepoManager {
repo.setAttribute("icon", "blah.png");
repo.setAttribute("maxage", String.valueOf(repoMaxAge));
repo.setAttribute("name", repoName + " on " + ipAddressString);
+ repo.setAttribute("pubkey", Hasher.hex(LocalRepoKeyStore.get(context).getCertificate()));
long timestamp = System.currentTimeMillis() / 1000L;
repo.setAttribute("timestamp", String.valueOf(timestamp));
repo.setAttribute("url", uriString);
@@ -417,4 +426,30 @@ public class LocalRepoManager {
transformer.transform(domSource, result);
}
+
+ public void writeIndexJar() throws IOException {
+ BufferedOutputStream bo = new BufferedOutputStream(
+ new FileOutputStream(xmlIndexJarUnsigned));
+ JarOutputStream jo = new JarOutputStream(bo);
+
+ BufferedInputStream bi = new BufferedInputStream(new FileInputStream(xmlIndex));
+
+ JarEntry je = new JarEntry("index.xml");
+ jo.putNextEntry(je);
+
+ byte[] buf = new byte[1024];
+ int bytesRead;
+
+ while ((bytesRead = bi.read(buf)) != -1) {
+ jo.write(buf, 0, bytesRead);
+ }
+
+ bi.close();
+ jo.close();
+ bo.close();
+
+ LocalRepoKeyStore.get(context).signZip(xmlIndexJarUnsigned, xmlIndexJar);
+
+ xmlIndexJarUnsigned.delete();
+ }
}
diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java
index 690105fdc..3e783493b 100644
--- a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java
+++ b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java
@@ -201,7 +201,7 @@ public class LocalRepoService extends Service {
values.put("path", "/fdroid/repo");
values.put("name", repoName);
// TODO set type based on "use HTTPS" pref
- // values.put("fingerprint", FDroidApp.repo.fingerprint);
+ values.put("fingerprint", FDroidApp.repo.fingerprint);
values.put("type", "fdroidrepo");
pairService = ServiceInfo.create("_http._tcp.local.",
repoName, FDroidApp.port, 0, 0, values);
diff --git a/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/src/org/fdroid/fdroid/net/WifiStateChangeService.java
index 78b35abe6..f7cd2e111 100644
--- a/src/org/fdroid/fdroid/net/WifiStateChangeService.java
+++ b/src/org/fdroid/fdroid/net/WifiStateChangeService.java
@@ -2,7 +2,6 @@
package org.fdroid.fdroid.net;
import android.app.Service;
-import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.wifi.WifiInfo;
@@ -16,7 +15,9 @@ import android.util.Log;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
+import java.security.cert.Certificate;
import java.util.Locale;
public class WifiStateChangeService extends Service {
@@ -67,6 +68,8 @@ public class WifiStateChangeService extends Service {
FDroidApp.repo.name = Preferences.get().getLocalRepoName();
FDroidApp.repo.address = String.format(Locale.ENGLISH, "%s://%s:%d/fdroid/repo",
scheme, FDroidApp.ipAddressString, FDroidApp.port);
+ Certificate localCert = LocalRepoKeyStore.get(getApplication()).getCertificate();
+ FDroidApp.repo.fingerprint = Utils.calcFingerprint(localCert);
FDroidApp.localRepo.setUriString(FDroidApp.repo.address);
FDroidApp.localRepo.writeIndexPage(
Utils.getSharingUri(WifiStateChangeService.this, FDroidApp.repo).toString());
diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java
index b7d4e3f21..411caed54 100644
--- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java
+++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java
@@ -22,9 +22,17 @@ import android.view.*;
import android.widget.*;
import org.fdroid.fdroid.*;
+import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
import org.fdroid.fdroid.localrepo.LocalRepoService;
import org.fdroid.fdroid.net.WifiStateChangeService;
+import org.spongycastle.operator.OperatorCreationException;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
@@ -252,6 +260,28 @@ public class LocalRepoActivity extends Activity {
fingerprintTextView.setVisibility(View.GONE);
}
+ // Once the IP address is known we need to generate a self signed
+ // certificate to use for HTTPS that has a CN field set to the
+ // ipAddressString. We'll generate it even if useHttps is false
+ // to simplify having to detect when that preference changes.
+ try {
+ LocalRepoKeyStore.get(this).setupHTTPSCertificate();
+ } catch (UnrecoverableKeyException e) {
+ e.printStackTrace();
+ } catch (CertificateException e) {
+ e.printStackTrace();
+ } catch (OperatorCreationException e) {
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
// the required NFC API was added in 4.0 aka Ice Cream Sandwich
if (Build.VERSION.SDK_INT >= 14) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
@@ -300,6 +330,8 @@ public class LocalRepoActivity extends Activity {
FDroidApp.localRepo.writeIndexPage(sharingUri.toString());
publishProgress(getString(R.string.writing_index_xml));
FDroidApp.localRepo.writeIndexXML();
+ publishProgress(getString(R.string.writing_index_jar));
+ FDroidApp.localRepo.writeIndexJar();
publishProgress(getString(R.string.linking_apks));
FDroidApp.localRepo.copyApksToRepo();
publishProgress(getString(R.string.copying_icons));
diff --git a/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java b/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java
index d5b23c5ac..afd69cddf 100644
--- a/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java
+++ b/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java
@@ -200,8 +200,7 @@ public class RepoListFragment extends ListFragment
MenuItemCompat.SHOW_AS_ACTION_ALWAYS |
MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
- if (Build.VERSION.SDK_INT >= 16)
- {
+ if (Build.VERSION.SDK_INT >= 16) {
menu.add(Menu.NONE, SCAN_FOR_REPOS, 1, R.string.menu_scan_repo).setIcon(
android.R.drawable.ic_menu_search);
}
@@ -271,8 +270,7 @@ public class RepoListFragment extends ListFragment
path = "/fdroid/repo";
String serviceUrl = protocol + serviceInfo.getInetAddresses()[0]
+ ":" + serviceInfo.getPort() + path;
- // TODO get fingerprint from TXT record
- showAddRepo(serviceUrl, "");
+ showAddRepo(serviceUrl, serviceInfo.getPropertyString("fingerprint"));
}
});