/** * @return a new subsession, non-null * @param privateKeyStream null for transient, if non-null must have same encryption keys as * primary session and different signing keys * @param opts subsession options if any, may be null * @since 0.9.21 */ public I2PSession addSubsession(InputStream privateKeyStream, Properties opts) throws I2PSessionException { if (privateKeyStream == null) { // We don't actually need the same pubkey in the dest, just in the LS. // The dest one is unused. But this is how we find the LS keys // to reuse in RequestLeaseSetMessageHandler. ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024); try { SigType type = getSigType(opts); if (type != SigType.DSA_SHA1) { // hassle, have to set up the padding and cert, see I2PClientImpl throw new I2PSessionException("type " + type + " unsupported"); } PublicKey pub = _session.getMyDestination().getPublicKey(); PrivateKey priv = _session.getDecryptionKey(); SimpleDataStructure[] keys = _context.keyGenerator().generateSigningKeys(type); pub.writeBytes(keyStream); keys[0].writeBytes(keyStream); // signing pub Certificate.NULL_CERT.writeBytes(keyStream); priv.writeBytes(keyStream); keys[1].writeBytes(keyStream); // signing priv } catch (Exception e) { throw new I2PSessionException("Error creating keys", e); } privateKeyStream = new ByteArrayInputStream(keyStream.toByteArray()); } I2PSession rv = _session.addSubsession(privateKeyStream, opts); boolean added = _subsessions.add(rv); if (!added) { // shouldn't happen _session.removeSubsession(rv); throw new I2PSessionException("dup"); } ConnectionOptions defaultOptions = new ConnectionOptions(opts); int protocol = defaultOptions.getEnforceProtocol() ? I2PSession.PROTO_STREAMING : I2PSession.PROTO_ANY; rv.addMuxedSessionListener( _connectionManager.getMessageHandler(), protocol, defaultOptions.getLocalPort()); if (_log.shouldLog(Log.WARN)) _log.warn("Added subsession " + rv); return rv; }