- Change addCapabilities() to getCapabilities()
  - Add netdb family sign/verify utility (ticket #1510)
    (verify not yet used)
RouterInfo:
  - Remove addCapability() and delCapability()
StatPublisher:
  - Remove Service interface, not required
  - Consolidate getCapabilities() and network ID here
  - Add family signatures
  - Remove unused coreVersion and stat_uptime (as of 0.9.24)
This commit is contained in:
zzz
2015-12-10 13:03:49 +00:00
parent 77a6db1cab
commit 7e872088d0
9 changed files with 443 additions and 105 deletions

View File

@@ -434,44 +434,6 @@ public class RouterInfo extends DatabaseEntry {
return (bwTier);
}
/**
* Warning, must be called AFTER setOptions().
*
* @throws IllegalStateException if RouterInfo is already signed
*/
public void addCapability(char cap) {
if (_signature != null)
throw new IllegalStateException();
String caps = _options.getProperty(PROP_CAPABILITIES);
if (caps == null)
_options.setProperty(PROP_CAPABILITIES, String.valueOf(cap));
else if (caps.indexOf(cap) == -1)
_options.setProperty(PROP_CAPABILITIES, caps + cap);
}
/**
* @throws IllegalStateException if RouterInfo is already signed
* @deprecated unused
*/
public void delCapability(char cap) {
if (_signature != null)
throw new IllegalStateException();
String caps = _options.getProperty(PROP_CAPABILITIES);
int idx;
if (caps == null) {
return;
} else if ((idx = caps.indexOf(cap)) == -1) {
return;
} else {
StringBuilder buf = new StringBuilder(caps);
while ( (idx = buf.indexOf(""+cap)) != -1)
buf.deleteCharAt(idx);
_options.setProperty(PROP_CAPABILITIES, buf.toString());
}
}
/**
* Determine whether the router was published recently (within the given age milliseconds).
* The age should be large enough to take into consideration any clock fudge factor, so

View File

@@ -10,6 +10,7 @@ package net.i2p.router;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -35,6 +36,7 @@ import net.i2p.data.SigningPublicKey;
import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.CommSystemFacade.Status;
import net.i2p.router.crypto.FamilyKeyCrypto;
import net.i2p.router.message.GarlicMessageHandler;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.startup.CreateRouterInfoJob;
@@ -88,6 +90,8 @@ public class Router implements RouterClock.ClockShiftListener {
private final EventLog _eventLog;
private final Object _stateLock = new Object();
private State _state = State.UNINITIALIZED;
private FamilyKeyCrypto _familyKeyCrypto;
private boolean _familyKeyCryptoFail;
public final static String PROP_CONFIG_FILE = "router.configLocation";
@@ -830,7 +834,7 @@ public class Router implements RouterClock.ClockShiftListener {
* has changed.
*/
private void locked_rebuildRouterInfo(boolean blockingRebuild) {
RouterInfo ri = null;
RouterInfo ri;
if (_routerInfo != null)
ri = new RouterInfo(_routerInfo);
else
@@ -839,12 +843,10 @@ public class Router implements RouterClock.ClockShiftListener {
try {
ri.setPublished(_context.clock().now());
Properties stats = _context.statPublisher().publishStatistics();
stats.setProperty(RouterInfo.PROP_NETWORK_ID, NETWORK_ID+"");
ri.setOptions(stats);
ri.setAddresses(_context.commSystem().createAddresses());
addCapabilities(ri);
SigningPrivateKey key = _context.keyManager().getSigningPrivateKey();
if (key == null) {
_log.log(Log.CRIT, "Internal error - signing private key not known? Impossible?");
@@ -864,6 +866,32 @@ public class Router implements RouterClock.ClockShiftListener {
}
}
/**
* Family Key Crypto Signer / Verifier.
* Not for external use.
* If family key is set, first call Will take a while to generate keys.
* Warning - risk of deadlock - do not call while holding locks
* (other than routerInfoLock)
*
* @return null on initialization failure
* @since 0.9.24
*/
public FamilyKeyCrypto getFamilyKeyCrypto() {
synchronized (_routerInfoLock) {
if (_familyKeyCrypto == null) {
if (!_familyKeyCryptoFail) {
try {
_familyKeyCrypto = new FamilyKeyCrypto(_context);
} catch (GeneralSecurityException gse) {
_log.error("Failed to initialize family key crypto", gse);
_familyKeyCryptoFail = true;
}
}
}
}
return _familyKeyCrypto;
}
// publicize our ballpark capacity
public static final char CAPABILITY_BW12 = 'K';
public static final char CAPABILITY_BW32 = 'L';
@@ -887,14 +915,11 @@ public class Router implements RouterClock.ClockShiftListener {
/**
* For building our RI. Not for external use.
* This does not publish the ri.
* This does not use anything in the ri (i.e. it can be freshly constructed)
*
* TODO just return a string instead of passing in the RI? See PublishLocalRouterInfoJob.
*
* @param ri an unpublished ri we are generating.
* @return a capabilities string to be added to the RI
*/
public void addCapabilities(RouterInfo ri) {
String getCapabilities() {
StringBuilder rv = new StringBuilder(4);
int bwLim = Math.min(_context.bandwidthLimiter().getInboundKBytesPerSecond(),
_context.bandwidthLimiter().getOutboundKBytesPerSecond());
bwLim = (int)(bwLim * getSharePercentage());
@@ -903,40 +928,40 @@ public class Router implements RouterClock.ClockShiftListener {
String force = _context.getProperty(PROP_FORCE_BWCLASS);
if (force != null && force.length() > 0) {
ri.addCapability(force.charAt(0));
rv.append(force.charAt(0));
} else if (bwLim < 12) {
ri.addCapability(CAPABILITY_BW12);
rv.append(CAPABILITY_BW12);
} else if (bwLim <= 48) {
ri.addCapability(CAPABILITY_BW32);
rv.append(CAPABILITY_BW32);
} else if (bwLim <= 64) {
ri.addCapability(CAPABILITY_BW64);
rv.append(CAPABILITY_BW64);
} else if (bwLim <= 128) {
ri.addCapability(CAPABILITY_BW128);
rv.append(CAPABILITY_BW128);
} else if (bwLim <= 256) {
ri.addCapability(CAPABILITY_BW256);
rv.append(CAPABILITY_BW256);
} else if (bwLim <= 2000) { // TODO adjust threshold
// 512 supported as of 0.9.18;
// Add 256 as well for compatibility
ri.addCapability(CAPABILITY_BW512);
ri.addCapability(CAPABILITY_BW256);
rv.append(CAPABILITY_BW512);
rv.append(CAPABILITY_BW256);
} else {
// Unlimited supported as of 0.9.18;
// Add 256 as well for compatibility
ri.addCapability(CAPABILITY_BW_UNLIMITED);
ri.addCapability(CAPABILITY_BW256);
rv.append(CAPABILITY_BW_UNLIMITED);
rv.append(CAPABILITY_BW256);
}
// if prop set to true, don't tell people we are ff even if we are
if (_context.netDb().floodfillEnabled() &&
!_context.getBooleanProperty("router.hideFloodfillParticipant"))
ri.addCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL);
rv.append(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL);
if(_context.getBooleanProperty(PROP_HIDDEN))
ri.addCapability(RouterInfo.CAPABILITY_HIDDEN);
rv.append(RouterInfo.CAPABILITY_HIDDEN);
if (_context.getBooleanProperty(PROP_FORCE_UNREACHABLE)) {
ri.addCapability(CAPABILITY_UNREACHABLE);
return;
rv.append(CAPABILITY_UNREACHABLE);
return rv.toString();
}
switch (_context.commSystem().getStatus()) {
case OK:
@@ -946,13 +971,13 @@ public class Router implements RouterClock.ClockShiftListener {
case IPV4_DISABLED_IPV6_OK:
case IPV4_UNKNOWN_IPV6_OK:
case IPV4_SNAT_IPV6_OK:
ri.addCapability(CAPABILITY_REACHABLE);
rv.append(CAPABILITY_REACHABLE);
break;
case DIFFERENT:
case REJECT_UNSOLICITED:
case IPV4_DISABLED_IPV6_FIREWALLED:
ri.addCapability(CAPABILITY_UNREACHABLE);
rv.append(CAPABILITY_UNREACHABLE);
break;
case DISCONNECTED:
@@ -966,6 +991,7 @@ public class Router implements RouterClock.ClockShiftListener {
// no explicit capability
break;
}
return rv.toString();
}
/*
@@ -1178,7 +1204,6 @@ public class Router implements RouterClock.ClockShiftListener {
try { _context.clientManager().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the client manager", t); }
try { _context.namingService().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the naming service", t); }
try { _context.jobQueue().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the job queue", t); }
try { _context.statPublisher().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the stats publisher", t); }
try { _context.tunnelManager().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the tunnel manager", t); }
try { _context.tunnelDispatcher().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the tunnel dispatcher", t); }
try { _context.netDb().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the networkDb", t); }

View File

@@ -9,6 +9,7 @@ package net.i2p.router;
*/
import java.io.Writer;
import java.security.GeneralSecurityException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
@@ -16,6 +17,9 @@ import java.util.Properties;
import net.i2p.CoreVersion;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.crypto.FamilyKeyCrypto;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
@@ -25,15 +29,12 @@ import net.i2p.util.Log;
* Publishes some statistics about the router in the netDB.
*
*/
public class StatisticsManager implements Service {
public class StatisticsManager {
private final Log _log;
private final RouterContext _context;
public final static String PROP_PUBLISH_RANKINGS = "router.publishPeerRankings";
private static final String PROP_CONTACT_NAME = "netdb.contact";
private static final String PROP_FAMILY_NAME = "netdb.family.name";
private static final String PROP_FAMILY_KEY = "netdb.family.key";
private static final String PROP_FAMILY_SIG = "netdb.family.sig";
/** enhance anonymity by only including build stats one out of this many times */
private static final int RANDOM_INCLUDE_STATS = 16;
@@ -47,20 +48,32 @@ public class StatisticsManager implements Service {
_log = context.logManager().getLog(StatisticsManager.class);
}
/** noop */
public void shutdown() {}
/** noop */
public void restart() {}
/** noop */
public void startup() {}
/** Retrieve a snapshot of the statistics that should be published */
/**
* Retrieve a snapshot of the statistics that should be published.
*
* This includes all standard options (as of 0.9.24, network ID and caps)
*/
public Properties publishStatistics() {
// if hash is null, will be caught in fkc.sign()
return publishStatistics(_context.routerHash());
}
/**
* Retrieve a snapshot of the statistics that should be published.
*
* This includes all standard options (as of 0.9.24, network ID and caps)
*
* @param current router hash, non-null
* @since 0.9.24
*/
public Properties publishStatistics(Hash h) {
Properties stats = new Properties();
stats.setProperty("router.version", RouterVersion.VERSION);
stats.setProperty("coreVersion", CoreVersion.VERSION);
// scheduled for removal, never used
if (CoreVersion.VERSION.equals("0.9.23"))
stats.setProperty("coreVersion", CoreVersion.VERSION);
stats.setProperty(RouterInfo.PROP_NETWORK_ID, Integer.toString(Router.NETWORK_ID));
stats.setProperty(RouterInfo.PROP_CAPABILITIES, _context.router().getCapabilities());
// No longer expose, to make build tracking more expensive
// stats.setProperty("router.id", RouterVersion.ID);
@@ -152,8 +165,10 @@ public class StatisticsManager implements Service {
//includeRate("tunnel.acceptLoad", stats, new long[] { 10*60*1000 });
}
// So that we will still get build requests
stats.setProperty("stat_uptime", "90m");
// So that we will still get build requests - not required since 0.7.9 2010-01-12
// scheduled for removal
if (CoreVersion.VERSION.equals("0.9.23"))
stats.setProperty("stat_uptime", "90m");
if (FloodfillNetworkDatabaseFacade.isFloodfill(_context.router().getRouterInfo())) {
int ri = _context.router().getUptime() > 30*60*1000 ?
_context.netDb().getKnownRouters() :
@@ -168,16 +183,32 @@ public class StatisticsManager implements Service {
String contact = _context.getProperty(PROP_CONTACT_NAME);
if (contact != null)
stats.setProperty("contact", contact);
String family = _context.getProperty(PROP_FAMILY_NAME);
String family = _context.getProperty(FamilyKeyCrypto.PROP_FAMILY_NAME);
if (family != null) {
stats.setProperty("family", family);
// TODO
//String key = _context.getProperty(PROP_FAMILY_KEY);
//if (key != null) {
// get privkey
// sign something
// add b64 sig
//}
stats.setProperty(FamilyKeyCrypto.OPT_NAME, family);
String sig = null;
RouterInfo oldRI = _context.router().getRouterInfo();
if (oldRI != null) {
// don't do it if family changed
if (family.equals(oldRI.getOption(FamilyKeyCrypto.OPT_NAME))) {
// copy over the signature
sig = oldRI.getOption(FamilyKeyCrypto.OPT_SIG);
if (sig != null)
stats.setProperty(FamilyKeyCrypto.OPT_SIG, sig);
}
}
if (sig == null) {
FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto();
if (fkc != null) {
try {
sig = fkc.sign(family, h);
stats.setProperty(FamilyKeyCrypto.OPT_SIG, sig);
} catch (GeneralSecurityException gse) {
_log.error("Failed to sign router family", gse);
}
}
}
}
return stats;

View File

@@ -0,0 +1,324 @@
package net.i2p.router.crypto;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.CertUtil;
import net.i2p.crypto.KeyStoreUtil;
import net.i2p.crypto.SigType;
import net.i2p.crypto.SigUtil;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.router.StatisticsManager;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
/**
* Utilities for creating, storing, retrieving the signing keys for
* the netdb family feature
*
* @since 0.9.24
*/
public class FamilyKeyCrypto {
private final RouterContext _context;
private final Log _log;
private final Map<Hash, String> _verified;
private final Set<String> _negativeCache;
// following for verification only, otherwise null
private final String _fname;
private final SigningPrivateKey _privkey;
private static final String PROP_KEYSTORE_PASSWORD = "netdb.family.keystorePassword";
public static final String PROP_FAMILY_NAME = "netdb.family.name";
private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
private static final String PROP_KEY_PASSWORD = "netdb.family.keyPassword";
private static final String CERT_SUFFIX = ".crt";
private static final String KEYSTORE_PREFIX = "family-";
private static final String KEYSTORE_SUFFIX = ".ks";
private static final int DEFAULT_KEY_VALID_DAYS = 3652; // 10 years
private static final String DEFAULT_KEY_ALGORITHM = SigType.ECDSA_SHA256_P256.isAvailable() ? "EC" : "DSA";
private static final int DEFAULT_KEY_SIZE = SigType.ECDSA_SHA256_P256.isAvailable() ? 256 : 1024;
private static final String KS_DIR = "keystore";
private static final String CERT_DIR = "certificates/family";
public static final String OPT_NAME = "family";
public static final String OPT_SIG = "family.sig";
/**
* For signing and verification.
*
* If the context property netdb.family.name is set, this can be used for signing,
* else only for verification.
*/
public FamilyKeyCrypto(RouterContext context) throws GeneralSecurityException {
_context = context;
_log = _context.logManager().getLog(FamilyKeyCrypto.class);
_fname = _context.getProperty(PROP_FAMILY_NAME);
if (_fname != null) {
if (_fname.contains("/") || _fname.contains("\\") ||
_fname.contains("..") || (new File(_fname)).isAbsolute())
throw new GeneralSecurityException("Illegal family name");
}
_privkey = (_fname != null) ? initialize() : null;
_verified = new ConcurrentHashMap<Hash, String>(4);
_negativeCache = new ConcurrentHashSet<String>(4);
}
/**
* Create (if necessary) and load the key store, then run.
*/
private SigningPrivateKey initialize() throws GeneralSecurityException {
File dir = new SecureDirectory(_context.getConfigDir(), KS_DIR);
File keyStore = new File(dir, KEYSTORE_PREFIX + _fname + KEYSTORE_SUFFIX);
verifyKeyStore(keyStore);
return getPrivKey(keyStore);
}
/**
* Clears the caches
*/
public void shutdown() {
_verified.clear();
_negativeCache.clear();
}
/**
* Caller must add family to RI also.
* throws on all errors
*
* @param family non-null, must match that we were initialized with or will throw GSE
* @param h non-null
* @return non-null base 64 signature string to be added to the RI
* @throws GeneralSecurityException on null hash, null or changed family, or signing error
*/
public String sign(String family, Hash h) throws GeneralSecurityException {
if (_privkey == null)
throw new GeneralSecurityException("family name set, must restart router");
if (h == null)
throw new GeneralSecurityException("null router hash");
if (!_fname.equals(family))
throw new GeneralSecurityException("family name changed, must restart router");
byte[] nb = DataHelper.getUTF8(_fname);
int len = nb.length + Hash.HASH_LENGTH;
byte[] b = new byte[len];
System.arraycopy(nb, 0, b, 0, nb.length);
System.arraycopy(h.getData(), 0, b, nb.length, Hash.HASH_LENGTH);
Signature sig = _context.dsa().sign(b, _privkey);
if (sig == null)
throw new GeneralSecurityException("sig failed");
return sig.toBase64();
}
/**
* Verify the family signature in a RouterInfo.
* @return true if good sig or if no family specified at all
*/
public boolean verify(RouterInfo ri) {
String name = ri.getOption(OPT_NAME);
if (name == null)
return true;
String ssig = ri.getOption("OPT_SIG");
if (ssig == null)
return false;
Hash h = ri.getHash();
String nameAndSig = _verified.get(h);
String riNameAndSig = name + ssig;
if (nameAndSig != null) {
if (nameAndSig.equals(riNameAndSig))
return true;
// name or sig changed
_verified.remove(h);
}
if (_negativeCache.contains(name))
return false;
SigningPublicKey spk = loadCert(name);
if (spk == null) {
_negativeCache.add(name);
return false;
}
byte[] bsig = Base64.decode(ssig);
if (bsig == null)
return false;
Signature sig;
try {
sig = new Signature(spk.getType(), bsig);
} catch (IllegalArgumentException iae) {
// wrong size (type mismatch)
return false;
}
byte[] nb = DataHelper.getUTF8(_fname);
byte[] b = new byte[nb.length + Hash.HASH_LENGTH];
System.arraycopy(nb, 0, b, 0, nb.length);
System.arraycopy(ri.getHash().getData(), 0, b, nb.length, Hash.HASH_LENGTH);
boolean rv = _context.dsa().verifySignature(sig, b, spk);
if (rv)
_verified.put(h, riNameAndSig);
return rv;
}
/**
* @return success if it exists and we have a password, or it was created successfully.
* @throws GeneralSecurityException on keystore error
*/
private void verifyKeyStore(File ks) throws GeneralSecurityException {
if (ks.exists()) {
if (_context.getProperty(PROP_KEY_PASSWORD) == null) {
String s ="Family key error, must set " + PROP_KEY_PASSWORD + " in " +
(new File(_context.getConfigDir(), "router.config")).getAbsolutePath();
_log.error(s);
throw new GeneralSecurityException(s);
}
return;
}
File dir = ks.getParentFile();
if (!dir.exists()) {
File sdir = new SecureDirectory(dir.getAbsolutePath());
if (!sdir.mkdirs()) {
String s ="Family key error, must set " + PROP_KEY_PASSWORD + " in " +
(new File(_context.getConfigDir(), "router.config")).getAbsolutePath();
_log.error(s);
throw new GeneralSecurityException(s);
}
}
createKeyStore(ks);
// Now read it back out of the new keystore and save it in ascii form
// where the clients can get to it.
exportCert(ks);
}
/**
* Call out to keytool to create a new keystore with a keypair in it.
* Trying to do this programatically is a nightmare, requiring either BouncyCastle
* libs or using proprietary Sun libs, and it's a huge mess.
* If successful, stores the keystore password and key password in router.config.
*
* @throws GeneralSecurityException on all errors
*/
private void createKeyStore(File ks) throws GeneralSecurityException {
// make a random 48 character password (30 * 8 / 5)
String keyPassword = KeyStoreUtil.randomString();
// and one for the cname
String cname = _fname + ".family.i2p.net";
boolean success = KeyStoreUtil.createKeys(ks, DEFAULT_KEYSTORE_PASSWORD, _fname, cname, "family",
DEFAULT_KEY_VALID_DAYS, DEFAULT_KEY_ALGORITHM,
DEFAULT_KEY_SIZE, keyPassword);
if (success) {
success = ks.exists();
if (success) {
Map<String, String> changes = new HashMap<String, String>();
changes.put(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
changes.put(PROP_KEY_PASSWORD, keyPassword);
changes.put(PROP_FAMILY_NAME, _fname);
_context.router().saveConfig(changes, null);
}
}
if (success) {
_log.logAlways(Log.INFO, "Created new private key for netdb family \"" + _fname +
"\" in keystore: " + ks.getAbsolutePath() + "\n" +
"Copy the keystore to the other routers in the family,\n" +
"and add the following entries to their router.config file:\n" +
PROP_FAMILY_NAME + '=' + _fname + '\n' +
PROP_KEYSTORE_PASSWORD + '=' + DEFAULT_KEYSTORE_PASSWORD + '\n' +
PROP_KEY_PASSWORD + '=' + keyPassword);
} else {
String s = "Failed to create NetDb family keystore.\n" +
"This is for the Sun/Oracle keytool, others may be incompatible.\n" +
"If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD +
" to " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath();
_log.error(s);
throw new GeneralSecurityException(s);
}
}
/**
* Pull the cert back OUT of the keystore and save it as ascii
* so the clients can get to it.
*/
private void exportCert(File ks) {
File sdir = new SecureDirectory(_context.getConfigDir(), CERT_DIR);
if (sdir.exists() || sdir.mkdirs()) {
String ksPass = _context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
String name = _fname.replace("@", "_at_") + CERT_SUFFIX;
File out = new File(sdir, name);
boolean success = KeyStoreUtil.exportCert(ks, ksPass, _fname, out);
if (success) {
_log.logAlways(Log.INFO, "Created new public key certificate for netdb family \"" + _fname +
"\" in file: " + out.getAbsolutePath() + "\n" +
"The certificate will be associated with your router identity.\n" +
"Copy the certificate to the directory $I2P/" + CERT_DIR + " for each of the other routers in the family.\n" +
"Give this certificate to an I2P developer for inclusion in the next I2P release.");
} else {
_log.error("Error getting SSL cert to save as ASCII");
}
} else {
_log.error("Error saving ASCII SSL keys");
}
}
/**
* Load a public key from a cert.
*
* @return null on all errors
*/
private SigningPublicKey loadCert(String familyName) {
if (familyName.contains("/") || familyName.contains("\\") ||
familyName.contains("..") || (new File(familyName)).isAbsolute())
return null;
familyName = familyName.replace("@", "_at_");
File dir = new File(_context.getBaseDir(), CERT_DIR);
File file = new File(dir, familyName + CERT_SUFFIX);
if (!file.exists())
return null;
try {
PublicKey pk = CertUtil.loadKey(file);
return SigUtil.fromJavaKey(pk);
} catch (GeneralSecurityException gse) {
_log.error("Error loading family key " + familyName, gse);
} catch (IOException ioe) {
_log.error("Error loading family key " + familyName, ioe);
}
return null;
}
/**
* Get the private key from the keystore
* @return non-null, throws on all errors
*/
private SigningPrivateKey getPrivKey(File ks) throws GeneralSecurityException {
String ksPass = _context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
String keyPass = _context.getProperty(PROP_KEY_PASSWORD);
if (keyPass == null)
throw new GeneralSecurityException("No key password, set " + PROP_KEY_PASSWORD +
" in " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath());
try {
PrivateKey pk = KeyStoreUtil.getPrivateKey(ks, ksPass, _fname, keyPass);
if (pk == null)
throw new GeneralSecurityException("Family key not found: " + _fname);
return SigUtil.fromJavaKey(pk);
} catch (IOException ioe) {
throw new GeneralSecurityException("Error loading family key " + _fname, ioe);
}
}
}

View File

@@ -82,9 +82,6 @@ public class PublishLocalRouterInfoJob extends JobImpl {
List<RouterAddress> newAddrs = getContext().commSystem().createAddresses();
int count = _runCount.incrementAndGet();
RouterInfo ri = new RouterInfo(oldRI);
// this will get overwritten by setOptions() below, must restore it below
getContext().router().addCapabilities(ri);
String caps = ri.getCapabilities();
if (_notFirstTime && (count % 4) != 0 && oldAddrs.size() == newAddrs.size()) {
// 3 times out of 4, we don't republish if everything is the same...
// If something changed, including the cost, then publish,
@@ -116,9 +113,6 @@ public class PublishLocalRouterInfoJob extends JobImpl {
}
ri.setPublished(getContext().clock().now());
Properties stats = getContext().statPublisher().publishStatistics();
stats.setProperty(RouterInfo.PROP_NETWORK_ID, String.valueOf(Router.NETWORK_ID));
// restore caps generated above
stats.setProperty(RouterInfo.PROP_CAPABILITIES, caps);
ri.setOptions(stats);
ri.setAddresses(newAddrs);

View File

@@ -98,10 +98,6 @@ public class CreateRouterInfoJob extends JobImpl {
OutputStream fos1 = null;
try {
info.setAddresses(getContext().commSystem().createAddresses());
Properties stats = getContext().statPublisher().publishStatistics();
stats.setProperty(RouterInfo.PROP_NETWORK_ID, Router.NETWORK_ID+"");
getContext().router().addCapabilities(info);
info.setOptions(stats);
// not necessary, in constructor
//info.setPeers(new HashSet());
info.setPublished(getCurrentPublishDate(getContext()));
@@ -126,6 +122,8 @@ public class CreateRouterInfoJob extends JobImpl {
padding = null;
}
info.setIdentity(ident);
Properties stats = getContext().statPublisher().publishStatistics(ident.getHash());
info.setOptions(stats);
info.sign(signingPrivKey);

View File

@@ -31,6 +31,7 @@ import net.i2p.data.router.RouterPrivateKeyFile;
import net.i2p.router.JobImpl;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.crypto.FamilyKeyCrypto;
import net.i2p.router.networkdb.kademlia.PersistentDataStore;
import net.i2p.util.Log;
@@ -98,7 +99,13 @@ class LoadRouterInfoJob extends JobImpl {
throw new DataFormatException("Our RouterInfo has a bad signature");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reading in routerInfo from " + rif.getAbsolutePath() + " and it has " + info.getAddresses().size() + " addresses");
_us = info;
// don't reuse if family name changed
if (DataHelper.eq(info.getOption(FamilyKeyCrypto.OPT_NAME),
getContext().getProperty(FamilyKeyCrypto.PROP_FAMILY_NAME))) {
_us = info;
} else {
_log.logAlways(Log.WARN, "NetDb family name changed");
}
}
if (keys2Exist || keysExist) {

View File

@@ -118,10 +118,8 @@ class RebuildRouterInfoJob extends JobImpl {
try {
info.setAddresses(getContext().commSystem().createAddresses());
Properties stats = getContext().statPublisher().publishStatistics();
stats.setProperty(RouterInfo.PROP_NETWORK_ID, ""+Router.NETWORK_ID);
Properties stats = getContext().statPublisher().publishStatistics(info.getHash());
info.setOptions(stats);
getContext().router().addCapabilities(info);
// info.setPeers(new HashSet()); // this would have the trusted peers
info.setPublished(CreateRouterInfoJob.getCurrentPublishDate(getContext()));

View File

@@ -36,7 +36,6 @@ public class StartupJob extends JobImpl {
public void runJob() {
if (!SystemVersion.isAndroid())
getContext().jobQueue().addJob(new LoadClientAppsJob(getContext()));
getContext().statPublisher().startup();
getContext().jobQueue().addJob(new LoadRouterInfoJob(getContext()));
}
}