package net.i2p.client.impl; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.util.Properties; import net.i2p.I2PAppContext; import net.i2p.I2PException; import net.i2p.client.I2PClient; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.SigType; import net.i2p.data.Certificate; import net.i2p.data.Destination; import net.i2p.data.KeyCertificate; import net.i2p.data.PrivateKey; import net.i2p.data.PublicKey; import net.i2p.data.SigningPrivateKey; import net.i2p.data.SigningPublicKey; import net.i2p.data.SimpleDataStructure; import net.i2p.util.RandomSource; /** * Base client implementation. * An I2PClient contains no state, it is just a facility for creating private key files * and generating sesssions from existing private key files. * * @author jrandom */ public class I2PClientImpl implements I2PClient { /** * Create a destination with a DSA 1024/160 signature type and a null certificate. * This is not bound to the I2PClient, you must supply the data back again * in createSession(). * * Caller must close stream. * * @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey, * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile} */ public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException { return createDestination(destKeyStream, DEFAULT_SIGTYPE); } /** * Create a destination with the given signature type. * It will have a null certificate for DSA 1024/160 and KeyCertificate otherwise. * This is not bound to the I2PClient, you must supply the data back again * in createSession(). * * Caller must close stream. * * @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey, * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile} * @since 0.9.12 */ public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException { Certificate cert; if (type == SigType.DSA_SHA1) { cert = Certificate.NULL_CERT; } else { cert = new KeyCertificate(type); } return createDestination(destKeyStream, cert); } /** * Create the destination with the given payload and write it out along with * the PrivateKey and SigningPrivateKey to the destKeyStream * * If cert is a KeyCertificate, the signing keypair will be of the specified type. * The KeyCertificate data must be ............................. * The padding if any will be randomized. The extra key data if any will be set in the * key cert. * * Caller must close stream. * * @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey, * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile} */ public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException { Destination d = new Destination(); Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair(); PublicKey publicKey = (PublicKey) keypair[0]; PrivateKey privateKey = (PrivateKey) keypair[1]; SimpleDataStructure signingKeys[]; if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) { KeyCertificate kcert = cert.toKeyCertificate(); SigType type = kcert.getSigType(); try { signingKeys = KeyGenerator.getInstance().generateSigningKeys(type); } catch (GeneralSecurityException gse) { throw new I2PException("keygen fail", gse); } } else { signingKeys = KeyGenerator.getInstance().generateSigningKeys(); } SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0]; SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1]; d.setPublicKey(publicKey); d.setSigningPublicKey(signingPubKey); if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) { // fix up key certificate or padding KeyCertificate kcert = cert.toKeyCertificate(); SigType type = kcert.getSigType(); int len = type.getPubkeyLen(); if (len < 128) { byte[] pad = new byte[128 - len]; RandomSource.getInstance().nextBytes(pad); d.setPadding(pad); } else if (len > 128) { System.arraycopy(signingPubKey.getData(), 128, kcert.getPayload(), KeyCertificate.HEADER_LENGTH, len - 128); } } d.setCertificate(cert); d.writeBytes(destKeyStream); privateKey.writeBytes(destKeyStream); signingPrivKey.writeBytes(destKeyStream); destKeyStream.flush(); return d; } /** * Create a new session (though do not connect it yet) * * @param destKeyStream location from which to read the Destination, PrivateKey, and SigningPrivateKey from, * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile} * @param options set of options to configure the router with, if null will use System properties */ public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException { return createSession(I2PAppContext.getGlobalContext(), destKeyStream, options); } /** * Create a new session (though do not connect it yet) * * @param destKeyStream location from which to read the Destination, PrivateKey, and SigningPrivateKey from, * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile} * @param options set of options to configure the router with, if null will use System properties */ public I2PSession createSession(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException { return new I2PSessionMuxedImpl(context, destKeyStream, options); // thread safe and muxed } }