/** @return success */ private static final boolean genKeysCLI(String publicKeyFile, String privateKeyFile) { FileOutputStream fileOutputStream = null; I2PAppContext context = I2PAppContext.getGlobalContext(); try { Object signingKeypair[] = context.keyGenerator().generateSigningKeypair(); SigningPublicKey signingPublicKey = (SigningPublicKey) signingKeypair[0]; SigningPrivateKey signingPrivateKey = (SigningPrivateKey) signingKeypair[1]; fileOutputStream = new FileOutputStream(publicKeyFile); signingPublicKey.writeBytes(fileOutputStream); fileOutputStream.close(); fileOutputStream = null; fileOutputStream = new FileOutputStream(privateKeyFile); signingPrivateKey.writeBytes(fileOutputStream); System.out.println("\r\nPrivate key written to: " + privateKeyFile); System.out.println("Public key written to: " + publicKeyFile); System.out.println("\r\nPublic key: " + signingPublicKey.toBase64() + "\r\n"); } catch (Exception e) { System.err.println("Error writing keys:"); e.printStackTrace(); return false; } finally { if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ioe) { } } return true; }
/** * Verifies the DSA signature of a signed update file. * * @param signedFile The signed update file to check. * @param publicKeyFile A file containing the public key to use for verification. * @return <code>true</code> if the file has a valid signature, otherwise <code>false</code>. */ public boolean verify(String signedFile, String publicKeyFile) { SigningPublicKey signingPublicKey = new SigningPublicKey(); FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(signedFile); signingPublicKey.readBytes(fileInputStream); } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to load the signature", ioe); return false; } catch (DataFormatException dfe) { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to load the signature", dfe); return false; } finally { if (fileInputStream != null) try { fileInputStream.close(); } catch (IOException ioe) { } } return verify(new File(signedFile), signingPublicKey); }
/** * Fetches the trusted keys for the current instance. We could sort it but don't bother. * * @return A <code>String</code> containing the trusted keys, delimited by CR LF line breaks. */ public String getTrustedKeysString() { StringBuilder buf = new StringBuilder(1024); for (SigningPublicKey spk : _trustedKeys.keySet()) { // If something already buffered, first add line break. if (buf.length() > 0) buf.append("\r\n"); buf.append(spk.toBase64()); } return buf.toString(); }
/** * Do we know about the following key? * * @since 0.7.12 */ public boolean haveKey(String key) { if (key.length() != KEYSIZE_B64_BYTES) return false; SigningPublicKey signingPublicKey = new SigningPublicKey(); try { signingPublicKey.fromBase64(key); } catch (DataFormatException dfe) { return false; } return _trustedKeys.containsKey(signingPublicKey); }
/** Verify with the "olddest" property's public key using the "oldsig" property */ public boolean hasValidInnerSig() { if (props == null || name == null || dest == null) return false; boolean rv = false; // don't cache result if (true) { StringWriter buf = new StringWriter(1024); String sig = props.getProperty(PROP_OLDSIG); String olddest = props.getProperty(PROP_OLDDEST); if (sig == null || olddest == null) return false; buf.append(name); buf.append(KV_SEPARATOR); buf.append(dest); try { writeProps(buf, true, true); } catch (IOException ioe) { // won't happen return false; } byte[] sdata = Base64.decode(sig); if (sdata == null) return false; Destination d; try { d = new Destination(olddest); } catch (DataFormatException dfe) { return false; } SigningPublicKey spk = d.getSigningPublicKey(); SigType type = spk.getType(); if (type == null) return false; Signature s; try { s = new Signature(type, sdata); } catch (IllegalArgumentException iae) { return false; } rv = DSAEngine.getInstance().verifySignature(s, DataHelper.getUTF8(buf.toString()), spk); } return rv; }
/** * @param hash either a Hash or a SHA1Hash * @since 0.8.3 */ private boolean verifySig( Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) { long start = _context.clock().now(); try { byte[] sigbytes = signature.getData(); byte rbytes[] = new byte[20]; byte sbytes[] = new byte[20]; // System.arraycopy(sigbytes, 0, rbytes, 0, 20); // System.arraycopy(sigbytes, 20, sbytes, 0, 20); for (int x = 0; x < 40; x++) { if (x < 20) { rbytes[x] = sigbytes[x]; } else { sbytes[x - 20] = sigbytes[x]; } } BigInteger s = new NativeBigInteger(1, sbytes); BigInteger r = new NativeBigInteger(1, rbytes); BigInteger y = new NativeBigInteger(1, verifyingKey.getData()); BigInteger w = null; try { w = s.modInverse(CryptoConstants.dsaq); } catch (ArithmeticException ae) { _log.warn("modInverse() error", ae); return false; } byte data[] = hash.getData(); NativeBigInteger bi = new NativeBigInteger(1, data); BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq); BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq); BigInteger modval = CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap); BigInteger modmulval = modval.multiply(y.modPow(u2, CryptoConstants.dsap)); BigInteger v = (modmulval).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq); boolean ok = v.compareTo(r) == 0; long diff = _context.clock().now() - start; if (diff > 1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to verify the signature (" + diff + "ms)"); } return ok; } catch (Exception e) { _log.log(Log.CRIT, "Error verifying the signature", e); return false; } }
/** * Duplicate keys or names rejected, except that duplicate empty names are allowed * * @param key 172 character base64 string * @param name non-null but "" ok * @since 0.7.12 * @return true if successful */ public boolean addKey(String key, String name) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding " + name + ": " + key); SigningPublicKey signingPublicKey = new SigningPublicKey(); try { // fromBase64() will throw a DFE if length is not right signingPublicKey.fromBase64(key); } catch (DataFormatException dfe) { _log.error("Invalid signing key for " + name + " : " + key, dfe); return false; } String oldName = _trustedKeys.get(signingPublicKey); // already there? if (name.equals(oldName)) return true; if (oldName != null && !oldName.equals("")) { _log.error("Key for " + name + " already stored for different name " + oldName + " : " + key); return false; } if ((!name.equals("")) && _trustedKeys.containsValue(name)) { _log.error("Key mismatch for " + name + ", spoof attempt? : " + key); return false; } _trustedKeys.put(signingPublicKey, name); return true; }
/** * Alternate to verifySignature() using java.security libraries. * * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.8.7 */ private boolean altVerifySigSHA1(Signature signature, byte[] data, SigningPublicKey verifyingKey) throws GeneralSecurityException { java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA"); KeyFactory keyFact = KeyFactory.getInstance("DSA"); // y p q g KeySpec spec = new DSAPublicKeySpec( new NativeBigInteger(1, verifyingKey.getData()), CryptoConstants.dsap, CryptoConstants.dsaq, CryptoConstants.dsag); PublicKey pubKey = keyFact.generatePublic(spec); jsig.initVerify(pubKey); jsig.update(data); boolean rv = jsig.verify(sigBytesToASN1(signature.getData())); // if (!rv) { // System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData())); // System.out.println("BAD SIG\n" + // net.i2p.util.HexDump.dump(sigBytesToASN1(signature.getData()))); // } return rv; }
/** * Writes 6 files: router.info (standard RI format), router.keys.dat, and 4 individual key files * under keyBackup/ * * <p>router.keys.dat file format: This is the same "eepPriv.dat" format used by the client code, * as documented in PrivateKeyFile. * * <p>Old router.keys file format: Note that this is NOT the same "eepPriv.dat" format used by the * client code. * * <pre> * - Private key (256 bytes) * - Signing Private key (20 bytes) * - Public key (256 bytes) * - Signing Public key (128 bytes) * Total 660 bytes * </pre> * * Caller must hold Router.routerInfoFileLock. */ RouterInfo createRouterInfo() { SigType type = getSigTypeConfig(getContext()); RouterInfo info = new RouterInfo(); OutputStream fos1 = null; try { info.setAddresses(getContext().commSystem().createAddresses()); // not necessary, in constructor // info.setPeers(new HashSet()); info.setPublished(getCurrentPublishDate(getContext())); Object keypair[] = getContext().keyGenerator().generatePKIKeypair(); PublicKey pubkey = (PublicKey) keypair[0]; PrivateKey privkey = (PrivateKey) keypair[1]; SimpleDataStructure signingKeypair[] = getContext().keyGenerator().generateSigningKeys(type); SigningPublicKey signingPubKey = (SigningPublicKey) signingKeypair[0]; SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeypair[1]; RouterIdentity ident = new RouterIdentity(); Certificate cert = createCertificate(getContext(), signingPubKey); ident.setCertificate(cert); ident.setPublicKey(pubkey); ident.setSigningPublicKey(signingPubKey); byte[] padding; int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length(); if (padLen > 0) { padding = new byte[padLen]; getContext().random().nextBytes(padding); ident.setPadding(padding); } else { padding = null; } info.setIdentity(ident); Properties stats = getContext().statPublisher().publishStatistics(ident.getHash()); info.setOptions(stats); info.sign(signingPrivKey); if (!info.isValid()) throw new DataFormatException("RouterInfo we just built is invalid: " + info); // remove router.keys (new File(getContext().getRouterDir(), KEYS_FILENAME)).delete(); // write router.info File ifile = new File(getContext().getRouterDir(), INFO_FILENAME); fos1 = new BufferedOutputStream(new SecureFileOutputStream(ifile)); info.writeBytes(fos1); // write router.keys.dat File kfile = new File(getContext().getRouterDir(), KEYS2_FILENAME); PrivateKeyFile pkf = new PrivateKeyFile(kfile, pubkey, signingPubKey, cert, privkey, signingPrivKey, padding); pkf.write(); getContext().keyManager().setKeys(pubkey, privkey, signingPubKey, signingPrivKey); if (_log.shouldLog(Log.INFO)) _log.info( "Router info created and stored at " + ifile.getAbsolutePath() + " with private keys stored at " + kfile.getAbsolutePath() + " [" + info + "]"); getContext().router().eventLog().addEvent(EventLog.REKEYED, ident.calculateHash().toBase64()); } catch (GeneralSecurityException gse) { _log.log(Log.CRIT, "Error building the new router information", gse); } catch (DataFormatException dfe) { _log.log(Log.CRIT, "Error building the new router information", dfe); } catch (IOException ioe) { _log.log(Log.CRIT, "Error writing out the new router information", ioe); } finally { if (fos1 != null) try { fos1.close(); } catch (IOException ioe) { } } return info; }
/** * Only called at startup via LoadRouterInfoJob and RebuildRouterInfoJob. Not called by periodic * RepublishLocalRouterInfoJob. We don't want to change the cert on the fly as it changes the * router hash. RouterInfo.isHidden() checks the capability, but RouterIdentity.isHidden() checks * the cert. There's no reason to ever add a hidden cert? * * @return the certificate for a new RouterInfo - probably a null cert. * @since 0.9.16 moved from Router */ static Certificate createCertificate(RouterContext ctx, SigningPublicKey spk) { if (spk.getType() != SigType.DSA_SHA1) return new KeyCertificate(spk); if (ctx.getBooleanProperty(Router.PROP_HIDDEN)) return new Certificate(Certificate.CERTIFICATE_TYPE_HIDDEN, null); return Certificate.NULL_CERT; }