/* * Get the active protocol versions. * * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT * negotiate these cipher suites in TLS 1.1 or later mode. * * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the * only enabled cipher suite, the client cannot request TLS 1.1 or * later, even though TLS 1.1 or later is enabled. We need to create a * subset of the enabled protocols, called the active protocols, which * contains protocols appropriate to the list of enabled Ciphersuites. * * Return empty list instead of null if no active protocol versions. */ ProtocolList getActiveProtocols() { if (activeProtocols == null) { ArrayList<ProtocolVersion> protocols = new ArrayList<>(4); for (ProtocolVersion protocol : enabledProtocols.collection()) { boolean found = false; for (CipherSuite suite : enabledCipherSuites.collection()) { if (suite.isAvailable() && suite.obsoleted > protocol.v && suite.supported <= protocol.v) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { protocols.add(protocol); found = true; break; } else if (debug != null && Debug.isOn("verbose")) { System.out.println("Ignoring disabled cipher suite: " + suite + " for " + protocol); } } else if (debug != null && Debug.isOn("verbose")) { System.out.println("Ignoring unsupported cipher suite: " + suite + " for " + protocol); } } if (!found && (debug != null) && Debug.isOn("handshake")) { System.out.println("No available cipher suite for " + protocol); } } activeProtocols = new ProtocolList(protocols); } return activeProtocols; }
/* * Sends a change cipher spec message and updates the write side * cipher state so that future messages use the just-negotiated spec. */ void sendChangeCipherSpec(Finished mesg, boolean lastMessage) throws IOException { output.flush(); // i.e. handshake data /* * The write cipher state is protected by the connection write lock * so we must grab it while making the change. We also * make sure no writes occur between sending the ChangeCipherSpec * message, installing the new cipher state, and sending the * Finished message. * * We already hold SSLEngine/SSLSocket "this" by virtue * of this being called from the readRecord code. */ OutputRecord r; if (conn != null) { r = new OutputRecord(Record.ct_change_cipher_spec); } else { r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine); } r.setVersion(protocolVersion); r.write(1); // single byte of data if (conn != null) { conn.writeLock.lock(); try { conn.writeRecord(r); conn.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } mesg.write(output); output.flush(); } finally { conn.writeLock.unlock(); } } else { synchronized (engine.writeLock) { engine.writeRecord((EngineOutputRecord) r); engine.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } mesg.write(output); if (lastMessage) { output.setFinishedMsg(); } output.flush(); } } }
protected void sendRequest(PrintWriter out, String[] msg) { for (int i = 0; i < msg.length; i++) { if (Debug.get(Debug.Communication)) { System.err.println(">>>" + msg[i]); } out.print(msg[i] + crlf); } if (Debug.get(Debug.Communication)) { System.err.println(">>>"); } out.print(crlf); out.flush(); }
/** * Get the active cipher suites. * * <p>In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, such as * TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT negotiate these cipher suites in * TLS 1.1 or later mode. * * <p>Therefore, when the active protocols only include TLS 1.1 or later, the client cannot * request to negotiate those obsoleted cipher suites. That is, the obsoleted suites should not be * included in the client hello. So we need to create a subset of the enabled cipher suites, the * active cipher suites, which does not contain obsoleted cipher suites of the minimum active * protocol. * * <p>Return empty list instead of null if no active cipher suites. */ CipherSuiteList getActiveCipherSuites() { if (activeCipherSuites == null) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } ArrayList<CipherSuite> suites = new ArrayList<>(); if (!(activeProtocols.collection().isEmpty()) && activeProtocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : enabledCipherSuites.collection()) { if (suite.obsoleted > activeProtocols.min.v && suite.supported <= activeProtocols.max.v) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); } } else if (debug != null && Debug.isOn("verbose")) { if (suite.obsoleted <= activeProtocols.min.v) { System.out.println("Ignoring obsoleted cipher suite: " + suite); } else { System.out.println("Ignoring unsupported cipher suite: " + suite); } } } } activeCipherSuites = new CipherSuiteList(suites); } return activeCipherSuites; }
public SSLResult fingerprint() throws IOException, FingerprintError { SSLConfigCollector scc; scc = new SSLConfigCollector(host, port, si); scc.setCertValidator(cv); startDate = new Date(); sslSupport = SSLResult.UNKNOWN; // If a delay is set, wait some time, except for // the first request if (!initial && (delay > 0)) { if (Debug.get(Debug.Delay)) { System.err.println("Delaying request."); } try { Thread.sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } initial = false; try { scc.probe(); sslSupport = SSLResult.SUPPORTED; sslSupportReason = null; } catch (NoSSLException e) { // This exception is thrown when the protocol support // for ssl is not available sslSupport = SSLResult.UNSUPPORTED; sslSupportReason = e.toString(); } catch (FingerprintException e) { sslSupport = SSLResult.UNSUPPORTED; sslSupportReason = e.toString(); } catch (IOException e) { sslSupport = SSLResult.UNKNOWN; sslSupportReason = e.toString(); } endDate = new Date(); protos = scc.getSupportedProtos(); ProbeResult pres = new ProbeResult( host, port, startDate, endDate, sslSupport, sslSupportReason, scc.getServerCertificates(), scc.serverCertificateVerifies(), scc.serverCertNameMatch()); pres.setProtosResult(protos); return pres; }
public void setCredentials(String uid, String pass) { authID = uid; authPW = pass; if (Debug.get(Debug.SockInit)) { System.err.println("HttpProxySocketInitialiser: " + " uid " + authID + " pw " + authPW); } }
private void init( SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, byte[] clientVerifyData, byte[] serverVerifyData) { if (debug != null && Debug.isOn("handshake")) { System.out.println( "Allow unsafe renegotiation: " + allowUnsafeRenegotiation + "\nAllow legacy hello messages: " + allowLegacyHelloMessages + "\nIs initial handshake: " + isInitialHandshake + "\nIs secure renegotiation: " + secureRenegotiation); } this.sslContext = context; this.isClient = isClient; this.needCertVerify = needCertVerify; this.activeProtocolVersion = activeProtocolVersion; this.isInitialHandshake = isInitialHandshake; this.secureRenegotiation = secureRenegotiation; this.clientVerifyData = clientVerifyData; this.serverVerifyData = serverVerifyData; enableNewSession = true; invalidated = false; setCipherSuite(CipherSuite.C_NULL); setEnabledProtocols(enabledProtocols); if (conn != null) { algorithmConstraints = new SSLAlgorithmConstraints(conn, true); } else { // engine != null algorithmConstraints = new SSLAlgorithmConstraints(engine, true); } // // In addition to the connection state machine, controlling // how the connection deals with the different sorts of records // that get sent (notably handshake transitions!), there's // also a handshaking state machine that controls message // sequencing. // // It's a convenient artifact of the protocol that this can, // with only a couple of minor exceptions, be driven by the // type constant for the last message seen: except for the // client's cert verify, those constants are in a convenient // order to drastically simplify state machine checking. // state = -2; // initialized but not activated }
/* * Used to kickstart the negotiation ... either writing a * ClientHello or a HelloRequest as appropriate, whichever * the subclass returns. NOP if handshaking's already started. */ void kickstart() throws IOException { if (state >= 0) { return; } HandshakeMessage m = getKickstartMessage(); if (debug != null && Debug.isOn("handshake")) { m.print(System.out); } m.write(output); output.flush(); state = m.messageType(); }
protected void prepareAuthorisation(String authVal) throws FingerprintError { if (authVal == null) { throw new HttpProxyError("Authentication " + "challenge missing"); } int idx = authVal.indexOf(' '); String scheme = authVal.substring(0, idx).toLowerCase(); String param; if (authVal.length() > idx) { param = authVal.substring(idx + 1); } else { param = ""; } if (!scheme.equals("basic")) { throw new HttpProxyError("Auth scheme '" + scheme + "' not supported"); } if (Debug.get(Debug.SockInit)) { System.err.println("prepareAuthorisation: " + "using scheme " + scheme); } authenticator = "Basic " + Base64.encode(authID + ":" + authPW); if (Debug.get(Debug.SockInit)) { System.err.println("prepareAuthorisation: auth=" + authenticator); } }
/** * Handshaker ... processes handshake records from an SSL V3.0 data stream, handling all the details * of the handshake protocol. * * <p>Note that the real protocol work is done in two subclasses, the base class just provides the * control flow and key generation framework. * * @author David Brownell */ abstract class Handshaker { // protocol version being established using this Handshaker ProtocolVersion protocolVersion; // the currently active protocol version during a renegotiation ProtocolVersion activeProtocolVersion; // security parameters for secure renegotiation. boolean secureRenegotiation; byte[] clientVerifyData; byte[] serverVerifyData; // Is it an initial negotiation or a renegotiation? boolean isInitialHandshake; // List of enabled protocols private ProtocolList enabledProtocols; // List of enabled CipherSuites private CipherSuiteList enabledCipherSuites; // The endpoint identification protocol String identificationProtocol; // The cryptographic algorithm constraints private AlgorithmConstraints algorithmConstraints = null; // Local supported signature and algorithms Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs; // Peer supported signature and algorithms Collection<SignatureAndHashAlgorithm> peerSupportedSignAlgs; /* /* * List of active protocols * * Active protocols is a subset of enabled protocols, and will * contain only those protocols that have vaild cipher suites * enabled. */ private ProtocolList activeProtocols; /* * List of active cipher suites * * Active cipher suites is a subset of enabled cipher suites, and will * contain only those cipher suites available for the active protocols. */ private CipherSuiteList activeCipherSuites; private boolean isClient; private boolean needCertVerify; SSLSocketImpl conn = null; SSLEngineImpl engine = null; HandshakeHash handshakeHash; HandshakeInStream input; HandshakeOutStream output; int state; SSLContextImpl sslContext; RandomCookie clnt_random, svr_random; SSLSessionImpl session; // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL CipherSuite cipherSuite; // current key exchange. Never null, initially K_NULL KeyExchange keyExchange; /* True if this session is being resumed (fast handshake) */ boolean resumingSession; /* True if it's OK to start a new SSL session */ boolean enableNewSession; // Temporary storage for the individual keys. Set by // calculateConnectionKeys() and cleared once the ciphers are // activated. private SecretKey clntWriteKey, svrWriteKey; private IvParameterSpec clntWriteIV, svrWriteIV; private SecretKey clntMacSecret, svrMacSecret; /* * Delegated task subsystem data structures. * * If thrown is set, we need to propagate this back immediately * on entry into processMessage(). * * Data is protected by the SSLEngine.this lock. */ private volatile boolean taskDelegated = false; private volatile DelegatedTask delegatedTask = null; private volatile Exception thrown = null; // Could probably use a java.util.concurrent.atomic.AtomicReference // here instead of using this lock. Consider changing. private Object thrownLock = new Object(); /* Class and subclass dynamic debugging support */ static final Debug debug = Debug.getInstance("ssl"); // By default, disable the unsafe legacy session renegotiation static final boolean allowUnsafeRenegotiation = Debug.getBooleanProperty("sun.security.ssl.allowUnsafeRenegotiation", false); // For maximum interoperability and backward compatibility, RFC 5746 // allows server (or client) to accept ClientHello (or ServerHello) // message without the secure renegotiation_info extension or SCSV. // // For maximum security, RFC 5746 also allows server (or client) to // reject such message with a fatal "handshake_failure" alert. // // By default, allow such legacy hello messages. static final boolean allowLegacyHelloMessages = Debug.getBooleanProperty("sun.security.ssl.allowLegacyHelloMessages", true); // need to dispose the object when it is invalidated boolean invalidated; Handshaker( SSLSocketImpl c, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, byte[] clientVerifyData, byte[] serverVerifyData) { this.conn = c; init( context, enabledProtocols, needCertVerify, isClient, activeProtocolVersion, isInitialHandshake, secureRenegotiation, clientVerifyData, serverVerifyData); } Handshaker( SSLEngineImpl engine, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, byte[] clientVerifyData, byte[] serverVerifyData) { this.engine = engine; init( context, enabledProtocols, needCertVerify, isClient, activeProtocolVersion, isInitialHandshake, secureRenegotiation, clientVerifyData, serverVerifyData); } private void init( SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, byte[] clientVerifyData, byte[] serverVerifyData) { if (debug != null && Debug.isOn("handshake")) { System.out.println( "Allow unsafe renegotiation: " + allowUnsafeRenegotiation + "\nAllow legacy hello messages: " + allowLegacyHelloMessages + "\nIs initial handshake: " + isInitialHandshake + "\nIs secure renegotiation: " + secureRenegotiation); } this.sslContext = context; this.isClient = isClient; this.needCertVerify = needCertVerify; this.activeProtocolVersion = activeProtocolVersion; this.isInitialHandshake = isInitialHandshake; this.secureRenegotiation = secureRenegotiation; this.clientVerifyData = clientVerifyData; this.serverVerifyData = serverVerifyData; enableNewSession = true; invalidated = false; setCipherSuite(CipherSuite.C_NULL); setEnabledProtocols(enabledProtocols); if (conn != null) { algorithmConstraints = new SSLAlgorithmConstraints(conn, true); } else { // engine != null algorithmConstraints = new SSLAlgorithmConstraints(engine, true); } // // In addition to the connection state machine, controlling // how the connection deals with the different sorts of records // that get sent (notably handshake transitions!), there's // also a handshaking state machine that controls message // sequencing. // // It's a convenient artifact of the protocol that this can, // with only a couple of minor exceptions, be driven by the // type constant for the last message seen: except for the // client's cert verify, those constants are in a convenient // order to drastically simplify state machine checking. // state = -2; // initialized but not activated } /* * Reroutes calls to the SSLSocket or SSLEngine (*SE). * * We could have also done it by extra classes * and letting them override, but this seemed much * less involved. */ void fatalSE(byte b, String diagnostic) throws IOException { fatalSE(b, diagnostic, null); } void fatalSE(byte b, Throwable cause) throws IOException { fatalSE(b, null, cause); } void fatalSE(byte b, String diagnostic, Throwable cause) throws IOException { if (conn != null) { conn.fatal(b, diagnostic, cause); } else { engine.fatal(b, diagnostic, cause); } } void warningSE(byte b) { if (conn != null) { conn.warning(b); } else { engine.warning(b); } } String getRawHostnameSE() { if (conn != null) { return conn.getRawHostname(); } else { return engine.getPeerHost(); } } String getHostSE() { if (conn != null) { return conn.getHost(); } else { return engine.getPeerHost(); } } String getHostAddressSE() { if (conn != null) { return conn.getInetAddress().getHostAddress(); } else { /* * This is for caching only, doesn't matter that's is really * a hostname. The main thing is that it doesn't do * a reverse DNS lookup, potentially slowing things down. */ return engine.getPeerHost(); } } boolean isLoopbackSE() { if (conn != null) { return conn.getInetAddress().isLoopbackAddress(); } else { return false; } } int getPortSE() { if (conn != null) { return conn.getPort(); } else { return engine.getPeerPort(); } } int getLocalPortSE() { if (conn != null) { return conn.getLocalPort(); } else { return -1; } } AccessControlContext getAccSE() { if (conn != null) { return conn.getAcc(); } else { return engine.getAcc(); } } private void setVersionSE(ProtocolVersion protocolVersion) { if (conn != null) { conn.setVersion(protocolVersion); } else { engine.setVersion(protocolVersion); } } /** * Set the active protocol version and propagate it to the SSLSocket and our handshake streams. * Called from ClientHandshaker and ServerHandshaker with the negotiated protocol version. */ void setVersion(ProtocolVersion protocolVersion) { this.protocolVersion = protocolVersion; setVersionSE(protocolVersion); output.r.setVersion(protocolVersion); } /** * Set the enabled protocols. Called from the constructor or * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the handshake is not yet in progress). */ void setEnabledProtocols(ProtocolList enabledProtocols) { activeCipherSuites = null; activeProtocols = null; this.enabledProtocols = enabledProtocols; } /** * Set the enabled cipher suites. Called from SSLSocketImpl/SSLEngineImpl.setEnabledCipherSuites() * (if the handshake is not yet in progress). */ void setEnabledCipherSuites(CipherSuiteList enabledCipherSuites) { activeCipherSuites = null; activeProtocols = null; this.enabledCipherSuites = enabledCipherSuites; } /** * Set the algorithm constraints. Called from the constructor or * SSLSocketImpl/SSLEngineImpl.setAlgorithmConstraints() (if the handshake is not yet in * progress). */ void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) { activeCipherSuites = null; activeProtocols = null; this.algorithmConstraints = new SSLAlgorithmConstraints(algorithmConstraints); this.localSupportedSignAlgs = null; } Collection<SignatureAndHashAlgorithm> getLocalSupportedSignAlgs() { if (localSupportedSignAlgs == null) { localSupportedSignAlgs = SignatureAndHashAlgorithm.getSupportedAlgorithms(algorithmConstraints); } return localSupportedSignAlgs; } void setPeerSupportedSignAlgs(Collection<SignatureAndHashAlgorithm> algorithms) { peerSupportedSignAlgs = new ArrayList<SignatureAndHashAlgorithm>(algorithms); } Collection<SignatureAndHashAlgorithm> getPeerSupportedSignAlgs() { return peerSupportedSignAlgs; } /** * Set the identification protocol. Called from the constructor or * SSLSocketImpl/SSLEngineImpl.setIdentificationProtocol() (if the handshake is not yet in * progress). */ void setIdentificationProtocol(String protocol) { this.identificationProtocol = protocol; } /** * Prior to handshaking, activate the handshake and initialize the version, input stream and * output stream. */ void activate(ProtocolVersion helloVersion) throws IOException { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } if (activeProtocols.collection().isEmpty() || activeProtocols.max.v == ProtocolVersion.NONE.v) { throw new SSLHandshakeException("No appropriate protocol"); } if (activeCipherSuites == null) { activeCipherSuites = getActiveCipherSuites(); } if (activeCipherSuites.collection().isEmpty()) { throw new SSLHandshakeException("No appropriate cipher suite"); } // temporary protocol version until the actual protocol version // is negotiated in the Hello exchange. This affects the record // version we sent with the ClientHello. if (!isInitialHandshake) { protocolVersion = activeProtocolVersion; } else { protocolVersion = activeProtocols.max; } if (helloVersion == null || helloVersion.v == ProtocolVersion.NONE.v) { helloVersion = activeProtocols.helloVersion; } // We accumulate digests of the handshake messages so that // we can read/write CertificateVerify and Finished messages, // getting assurance against some particular active attacks. Set<String> localSupportedHashAlgorithms = SignatureAndHashAlgorithm.getHashAlgorithmNames(getLocalSupportedSignAlgs()); handshakeHash = new HandshakeHash(!isClient, needCertVerify, localSupportedHashAlgorithms); // Generate handshake input/output stream. input = new HandshakeInStream(handshakeHash); if (conn != null) { output = new HandshakeOutStream(protocolVersion, helloVersion, handshakeHash, conn); conn.getAppInputStream().r.setHandshakeHash(handshakeHash); conn.getAppInputStream().r.setHelloVersion(helloVersion); conn.getAppOutputStream().r.setHelloVersion(helloVersion); } else { output = new HandshakeOutStream(protocolVersion, helloVersion, handshakeHash, engine); engine.inputRecord.setHandshakeHash(handshakeHash); engine.inputRecord.setHelloVersion(helloVersion); engine.outputRecord.setHelloVersion(helloVersion); } // move state to activated state = -1; } /** * Set cipherSuite and keyExchange to the given CipherSuite. Does not perform any verification * that this is a valid selection, this must be done before calling this method. */ void setCipherSuite(CipherSuite s) { this.cipherSuite = s; this.keyExchange = s.keyExchange; } /** * Check if the given ciphersuite is enabled and available. Does not check if the required server * certificates are available. */ boolean isNegotiable(CipherSuite s) { if (activeCipherSuites == null) { activeCipherSuites = getActiveCipherSuites(); } return activeCipherSuites.contains(s) && s.isNegotiable(); } /** Check if the given protocol version is enabled and available. */ boolean isNegotiable(ProtocolVersion protocolVersion) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } return activeProtocols.contains(protocolVersion); } /** * Select a protocol version from the list. Called from ServerHandshaker to negotiate protocol * version. * * <p>Return the lower of the protocol version suggested in the clien hello and the highest * supported by the server. */ ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } return activeProtocols.selectProtocolVersion(protocolVersion); } /** * Get the active cipher suites. * * <p>In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, such as * TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT negotiate these cipher suites in * TLS 1.1 or later mode. * * <p>Therefore, when the active protocols only include TLS 1.1 or later, the client cannot * request to negotiate those obsoleted cipher suites. That is, the obsoleted suites should not be * included in the client hello. So we need to create a subset of the enabled cipher suites, the * active cipher suites, which does not contain obsoleted cipher suites of the minimum active * protocol. * * <p>Return empty list instead of null if no active cipher suites. */ CipherSuiteList getActiveCipherSuites() { if (activeCipherSuites == null) { if (activeProtocols == null) { activeProtocols = getActiveProtocols(); } ArrayList<CipherSuite> suites = new ArrayList<>(); if (!(activeProtocols.collection().isEmpty()) && activeProtocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : enabledCipherSuites.collection()) { if (suite.obsoleted > activeProtocols.min.v && suite.supported <= activeProtocols.max.v) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); } } else if (debug != null && Debug.isOn("verbose")) { if (suite.obsoleted <= activeProtocols.min.v) { System.out.println("Ignoring obsoleted cipher suite: " + suite); } else { System.out.println("Ignoring unsupported cipher suite: " + suite); } } } } activeCipherSuites = new CipherSuiteList(suites); } return activeCipherSuites; } /* * Get the active protocol versions. * * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT * negotiate these cipher suites in TLS 1.1 or later mode. * * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the * only enabled cipher suite, the client cannot request TLS 1.1 or * later, even though TLS 1.1 or later is enabled. We need to create a * subset of the enabled protocols, called the active protocols, which * contains protocols appropriate to the list of enabled Ciphersuites. * * Return empty list instead of null if no active protocol versions. */ ProtocolList getActiveProtocols() { if (activeProtocols == null) { ArrayList<ProtocolVersion> protocols = new ArrayList<>(4); for (ProtocolVersion protocol : enabledProtocols.collection()) { boolean found = false; for (CipherSuite suite : enabledCipherSuites.collection()) { if (suite.isAvailable() && suite.obsoleted > protocol.v && suite.supported <= protocol.v) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { protocols.add(protocol); found = true; break; } else if (debug != null && Debug.isOn("verbose")) { System.out.println("Ignoring disabled cipher suite: " + suite + " for " + protocol); } } else if (debug != null && Debug.isOn("verbose")) { System.out.println("Ignoring unsupported cipher suite: " + suite + " for " + protocol); } } if (!found && (debug != null) && Debug.isOn("handshake")) { System.out.println("No available cipher suite for " + protocol); } } activeProtocols = new ProtocolList(protocols); } return activeProtocols; } /** * As long as handshaking has not activated, we can change whether session creations are allowed. * * <p>Callers should do their own checking if handshaking has activated. */ void setEnableSessionCreation(boolean newSessions) { enableNewSession = newSessions; } /** Create a new read cipher and return it to caller. */ CipherBox newReadCipher() throws NoSuchAlgorithmException { BulkCipher cipher = cipherSuite.cipher; CipherBox box; if (isClient) { box = cipher.newCipher( protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), false); svrWriteKey = null; svrWriteIV = null; } else { box = cipher.newCipher( protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), false); clntWriteKey = null; clntWriteIV = null; } return box; } /** Create a new write cipher and return it to caller. */ CipherBox newWriteCipher() throws NoSuchAlgorithmException { BulkCipher cipher = cipherSuite.cipher; CipherBox box; if (isClient) { box = cipher.newCipher( protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), true); clntWriteKey = null; clntWriteIV = null; } else { box = cipher.newCipher( protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), true); svrWriteKey = null; svrWriteIV = null; } return box; } /** Create a new read MAC and return it to caller. */ MAC newReadMAC() throws NoSuchAlgorithmException, InvalidKeyException { MacAlg macAlg = cipherSuite.macAlg; MAC mac; if (isClient) { mac = macAlg.newMac(protocolVersion, svrMacSecret); svrMacSecret = null; } else { mac = macAlg.newMac(protocolVersion, clntMacSecret); clntMacSecret = null; } return mac; } /** Create a new write MAC and return it to caller. */ MAC newWriteMAC() throws NoSuchAlgorithmException, InvalidKeyException { MacAlg macAlg = cipherSuite.macAlg; MAC mac; if (isClient) { mac = macAlg.newMac(protocolVersion, clntMacSecret); clntMacSecret = null; } else { mac = macAlg.newMac(protocolVersion, svrMacSecret); svrMacSecret = null; } return mac; } /* * Returns true iff the handshake sequence is done, so that * this freshly created session can become the current one. */ boolean isDone() { return state == HandshakeMessage.ht_finished; } /* * Returns the session which was created through this * handshake sequence ... should be called after isDone() * returns true. */ SSLSessionImpl getSession() { return session; } /* * Set the handshake session */ void setHandshakeSessionSE(SSLSessionImpl handshakeSession) { if (conn != null) { conn.setHandshakeSession(handshakeSession); } else { engine.setHandshakeSession(handshakeSession); } } /* * Returns true if renegotiation is in use for this connection. */ boolean isSecureRenegotiation() { return secureRenegotiation; } /* * Returns the verify_data from the Finished message sent by the client. */ byte[] getClientVerifyData() { return clientVerifyData; } /* * Returns the verify_data from the Finished message sent by the server. */ byte[] getServerVerifyData() { return serverVerifyData; } /* * This routine is fed SSL handshake records when they become available, * and processes messages found therein. */ void process_record(InputRecord r, boolean expectingFinished) throws IOException { checkThrown(); /* * Store the incoming handshake data, then see if we can * now process any completed handshake messages */ input.incomingRecord(r); /* * We don't need to create a separate delegatable task * for finished messages. */ if ((conn != null) || expectingFinished) { processLoop(); } else { delegateTask( new PrivilegedExceptionAction<Void>() { public Void run() throws Exception { processLoop(); return null; } }); } } /* * On input, we hash messages one at a time since servers may need * to access an intermediate hash to validate a CertificateVerify * message. * * Note that many handshake messages can come in one record (and often * do, to reduce network resource utilization), and one message can also * require multiple records (e.g. very large Certificate messages). */ void processLoop() throws IOException { // need to read off 4 bytes at least to get the handshake // message type and length. while (input.available() >= 4) { byte messageType; int messageLen; /* * See if we can read the handshake message header, and * then the entire handshake message. If not, wait till * we can read and process an entire message. */ input.mark(4); messageType = (byte) input.getInt8(); messageLen = input.getInt24(); if (input.available() < messageLen) { input.reset(); return; } /* * Process the messsage. We require * that processMessage() consumes the entire message. In * lieu of explicit error checks (how?!) we assume that the * data will look like garbage on encoding/processing errors, * and that other protocol code will detect such errors. * * Note that digesting is normally deferred till after the * message has been processed, though to process at least the * client's Finished message (i.e. send the server's) we need * to acccelerate that digesting. * * Also, note that hello request messages are never hashed; * that includes the hello request header, too. */ if (messageType == HandshakeMessage.ht_hello_request) { input.reset(); processMessage(messageType, messageLen); input.ignore(4 + messageLen); } else { input.mark(messageLen); processMessage(messageType, messageLen); input.digestNow(); } } } /** * Returns true iff the handshaker has been activated. * * <p>In activated state, the handshaker may not send any messages out. */ boolean activated() { return state >= -1; } /** Returns true iff the handshaker has sent any messages. */ boolean started() { return state >= 0; // 0: HandshakeMessage.ht_hello_request // 1: HandshakeMessage.ht_client_hello } /* * Used to kickstart the negotiation ... either writing a * ClientHello or a HelloRequest as appropriate, whichever * the subclass returns. NOP if handshaking's already started. */ void kickstart() throws IOException { if (state >= 0) { return; } HandshakeMessage m = getKickstartMessage(); if (debug != null && Debug.isOn("handshake")) { m.print(System.out); } m.write(output); output.flush(); state = m.messageType(); } /** * Both client and server modes can start handshaking; but the message they send to do so is * different. */ abstract HandshakeMessage getKickstartMessage() throws SSLException; /* * Client and Server side protocols are each driven though this * call, which processes a single message and drives the appropriate * side of the protocol state machine (depending on the subclass). */ abstract void processMessage(byte messageType, int messageLen) throws IOException; /* * Most alerts in the protocol relate to handshaking problems. * Alerts are detected as the connection reads data. */ abstract void handshakeAlert(byte description) throws SSLProtocolException; /* * Sends a change cipher spec message and updates the write side * cipher state so that future messages use the just-negotiated spec. */ void sendChangeCipherSpec(Finished mesg, boolean lastMessage) throws IOException { output.flush(); // i.e. handshake data /* * The write cipher state is protected by the connection write lock * so we must grab it while making the change. We also * make sure no writes occur between sending the ChangeCipherSpec * message, installing the new cipher state, and sending the * Finished message. * * We already hold SSLEngine/SSLSocket "this" by virtue * of this being called from the readRecord code. */ OutputRecord r; if (conn != null) { r = new OutputRecord(Record.ct_change_cipher_spec); } else { r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine); } r.setVersion(protocolVersion); r.write(1); // single byte of data if (conn != null) { conn.writeLock.lock(); try { conn.writeRecord(r); conn.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } mesg.write(output); output.flush(); } finally { conn.writeLock.unlock(); } } else { synchronized (engine.writeLock) { engine.writeRecord((EngineOutputRecord) r); engine.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } mesg.write(output); if (lastMessage) { output.setFinishedMsg(); } output.flush(); } } } /* * Single access point to key calculation logic. Given the * pre-master secret and the nonces from client and server, * produce all the keying material to be used. */ void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) { SecretKey master = calculateMasterSecret(preMasterSecret, version); session.setMasterSecret(master); calculateConnectionKeys(master); } /* * Calculate the master secret from its various components. This is * used for key exchange by all cipher suites. * * The master secret is the catenation of three MD5 hashes, each * consisting of the pre-master secret and a SHA1 hash. Those three * SHA1 hashes are of (different) constant strings, the pre-master * secret, and the nonces provided by the client and the server. */ private SecretKey calculateMasterSecret( SecretKey preMasterSecret, ProtocolVersion requestedVersion) { if (debug != null && Debug.isOn("keygen")) { HexDumpEncoder dump = new HexDumpEncoder(); System.out.println("SESSION KEYGEN:"); System.out.println("PreMaster Secret:"); printHex(dump, preMasterSecret.getEncoded()); // Nonces are dumped with connection keygen, no // benefit to doing it twice } // What algs/params do we need to use? String masterAlg; PRF prf; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { masterAlg = "SunTls12MasterSecret"; prf = cipherSuite.prfAlg; } else { masterAlg = "SunTlsMasterSecret"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( preMasterSecret, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, prfHashAlg, prfHashLength, prfBlockSize); SecretKey masterSecret; try { KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); kg.init(spec); masterSecret = kg.generateKey(); } catch (GeneralSecurityException e) { // For RSA premaster secrets, do not signal a protocol error // due to the Bleichenbacher attack. See comments further down. if (!preMasterSecret.getAlgorithm().equals("TlsRsaPremasterSecret")) { throw new ProviderException(e); } if (debug != null && Debug.isOn("handshake")) { System.out.println("RSA master secret generation error:"); e.printStackTrace(System.out); System.out.println("Generating new random premaster secret"); } if (requestedVersion != null) { preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion); } else { preMasterSecret = RSAClientKeyExchange.generateDummySecret(protocolVersion); } // recursive call with new premaster secret return calculateMasterSecret(preMasterSecret, null); } // if no version check requested (client side handshake), or version // information is not available (not an RSA premaster secret), // return master secret immediately. if ((requestedVersion == null) || !(masterSecret instanceof TlsMasterSecret)) { return masterSecret; } // we have checked the ClientKeyExchange message when reading TLS // record, the following check is necessary to ensure that // JCE provider does not ignore the checking, or the previous // checking process bypassed the premaster secret version checking. TlsMasterSecret tlsKey = (TlsMasterSecret) masterSecret; int major = tlsKey.getMajorVersion(); int minor = tlsKey.getMinorVersion(); if ((major < 0) || (minor < 0)) { return masterSecret; } // check if the premaster secret version is ok // the specification says that it must be the maximum version supported // by the client from its ClientHello message. However, many // implementations send the negotiated version, so accept both // for SSL v3.0 and TLS v1.0. // NOTE that we may be comparing two unsupported version numbers, which // is why we cannot use object reference equality in this special case. ProtocolVersion premasterVersion = ProtocolVersion.valueOf(major, minor); boolean versionMismatch = (premasterVersion.v != requestedVersion.v); /* * we never checked the client_version in server side * for TLS v1.0 and SSL v3.0. For compatibility, we * maintain this behavior. */ if (versionMismatch && requestedVersion.v <= ProtocolVersion.TLS10.v) { versionMismatch = (premasterVersion.v != protocolVersion.v); } if (versionMismatch == false) { // check passed, return key return masterSecret; } // Due to the Bleichenbacher attack, do not signal a protocol error. // Generate a random premaster secret and continue with the handshake, // which will fail when verifying the finished messages. // For more information, see comments in PreMasterSecret. if (debug != null && Debug.isOn("handshake")) { System.out.println( "RSA PreMasterSecret version error: expected" + protocolVersion + " or " + requestedVersion + ", decrypted: " + premasterVersion); System.out.println("Generating new random premaster secret"); } preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion); // recursive call with new premaster secret return calculateMasterSecret(preMasterSecret, null); } /* * Calculate the keys needed for this connection, once the session's * master secret has been calculated. Uses the master key and nonces; * the amount of keying material generated is a function of the cipher * suite that's been negotiated. * * This gets called both on the "full handshake" (where we exchanged * a premaster secret and started a new session) as well as on the * "fast handshake" (where we just resumed a pre-existing session). */ void calculateConnectionKeys(SecretKey masterKey) { /* * For both the read and write sides of the protocol, we use the * master to generate MAC secrets and cipher keying material. Block * ciphers need initialization vectors, which we also generate. * * First we figure out how much keying material is needed. */ int hashSize = cipherSuite.macAlg.size; boolean is_exportable = cipherSuite.exportable; BulkCipher cipher = cipherSuite.cipher; int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; // Which algs/params do we need to use? String keyMaterialAlg; PRF prf; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { keyMaterialAlg = "SunTls12KeyMaterial"; prf = cipherSuite.prfAlg; } else { keyMaterialAlg = "SunTlsKeyMaterial"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( masterKey, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, cipher.algorithm, cipher.keySize, expandedKeySize, cipher.ivSize, hashSize, prfHashAlg, prfHashLength, prfBlockSize); try { KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); kg.init(spec); TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec) kg.generateKey(); clntWriteKey = keySpec.getClientCipherKey(); svrWriteKey = keySpec.getServerCipherKey(); // Return null if IVs are not supposed to be generated. // e.g. TLS 1.1+. clntWriteIV = keySpec.getClientIv(); svrWriteIV = keySpec.getServerIv(); clntMacSecret = keySpec.getClientMacKey(); svrMacSecret = keySpec.getServerMacKey(); } catch (GeneralSecurityException e) { throw new ProviderException(e); } // // Dump the connection keys as they're generated. // if (debug != null && Debug.isOn("keygen")) { synchronized (System.out) { HexDumpEncoder dump = new HexDumpEncoder(); System.out.println("CONNECTION KEYGEN:"); // Inputs: System.out.println("Client Nonce:"); printHex(dump, clnt_random.random_bytes); System.out.println("Server Nonce:"); printHex(dump, svr_random.random_bytes); System.out.println("Master Secret:"); printHex(dump, masterKey.getEncoded()); // Outputs: System.out.println("Client MAC write Secret:"); printHex(dump, clntMacSecret.getEncoded()); System.out.println("Server MAC write Secret:"); printHex(dump, svrMacSecret.getEncoded()); if (clntWriteKey != null) { System.out.println("Client write key:"); printHex(dump, clntWriteKey.getEncoded()); System.out.println("Server write key:"); printHex(dump, svrWriteKey.getEncoded()); } else { System.out.println("... no encryption keys used"); } if (clntWriteIV != null) { System.out.println("Client write IV:"); printHex(dump, clntWriteIV.getIV()); System.out.println("Server write IV:"); printHex(dump, svrWriteIV.getIV()); } else { if (protocolVersion.v >= ProtocolVersion.TLS11.v) { System.out.println("... no IV derived for this protocol"); } else { System.out.println("... no IV used for this cipher"); } } System.out.flush(); } } } private static void printHex(HexDumpEncoder dump, byte[] bytes) { if (bytes == null) { System.out.println("(key bytes not available)"); } else { try { dump.encodeBuffer(bytes, System.out); } catch (IOException e) { // just for debugging, ignore this } } } /** * Throw an SSLException with the specified message and cause. Shorthand until a new SSLException * constructor is added. This method never returns. */ static void throwSSLException(String msg, Throwable cause) throws SSLException { SSLException e = new SSLException(msg); e.initCause(cause); throw e; } /* * Implement a simple task delegator. * * We are currently implementing this as a single delegator, may * try for parallel tasks later. Client Authentication could * benefit from this, where ClientKeyExchange/CertificateVerify * could be carried out in parallel. */ class DelegatedTask<E> implements Runnable { private PrivilegedExceptionAction<E> pea; DelegatedTask(PrivilegedExceptionAction<E> pea) { this.pea = pea; } public void run() { synchronized (engine) { try { AccessController.doPrivileged(pea, engine.getAcc()); } catch (PrivilegedActionException pae) { thrown = pae.getException(); } catch (RuntimeException rte) { thrown = rte; } delegatedTask = null; taskDelegated = false; } } } private <T> void delegateTask(PrivilegedExceptionAction<T> pea) { delegatedTask = new DelegatedTask<T>(pea); taskDelegated = false; thrown = null; } DelegatedTask getTask() { if (!taskDelegated) { taskDelegated = true; return delegatedTask; } else { return null; } } /* * See if there are any tasks which need to be delegated * * Locked by SSLEngine.this. */ boolean taskOutstanding() { return (delegatedTask != null); } /* * The previous caller failed for some reason, report back the * Exception. We won't worry about Error's. * * Locked by SSLEngine.this. */ void checkThrown() throws SSLException { synchronized (thrownLock) { if (thrown != null) { String msg = thrown.getMessage(); if (msg == null) { msg = "Delegated task threw Exception/Error"; } /* * See what the underlying type of exception is. We should * throw the same thing. Chain thrown to the new exception. */ Exception e = thrown; thrown = null; if (e instanceof RuntimeException) { throw (RuntimeException) new RuntimeException(msg).initCause(e); } else if (e instanceof SSLHandshakeException) { throw (SSLHandshakeException) new SSLHandshakeException(msg).initCause(e); } else if (e instanceof SSLKeyException) { throw (SSLKeyException) new SSLKeyException(msg).initCause(e); } else if (e instanceof SSLPeerUnverifiedException) { throw (SSLPeerUnverifiedException) new SSLPeerUnverifiedException(msg).initCause(e); } else if (e instanceof SSLProtocolException) { throw (SSLProtocolException) new SSLProtocolException(msg).initCause(e); } else { /* * If it's SSLException or any other Exception, * we'll wrap it in an SSLException. */ throw (SSLException) new SSLException(msg).initCause(e); } } } } }
public void runSupport() { try { new URL(url_str); // determine if this is already a proper URL } catch (Throwable t) { // it's not // //check if the string is just a base32/hex-encoded torrent infohash // // String magnet_uri = UrlUtils.normaliseMagnetURI( url_str ); // // if ( magnet_uri != null ){ // // url_str = magnet_uri; // } } try { url = AddressUtils.adjustURL(new URL(url_str)); String protocol = url.getProtocol().toLowerCase(); // hack here - the magnet download process requires an additional paramter to cause it to // stall on error so the error can be reported // if ( protocol.equals( "magnet" ) || protocol.equals( "dht" )){ // // url = AddressUtils.adjustURL( new URL(url_str+"&pause_on_error=true")); // } for (int i = 0; i < 2; i++) { try { // if ( protocol.equals("https")){ // // // see ConfigurationChecker for SSL client defaults // // HttpsURLConnection ssl_con = (HttpsURLConnection)url.openConnection(); // // // allow for certs that contain IP addresses rather than dns names // // ssl_con.setHostnameVerifier( // new HostnameVerifier() // { // public boolean // verify( // String host, // SSLSession session ) // { // return( true ); // } // }); // // con = ssl_con; // // }else{ // con = (HttpURLConnection) url.openConnection(); // } con.setRequestProperty( "User-Agent", Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION); if (referrer != null && referrer.length() > 0) { con.setRequestProperty("Referer", referrer); } if (request_properties != null) { Iterator it = request_properties.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String key = (String) entry.getKey(); String value = (String) entry.getValue(); // currently this code doesn't support gzip/deflate... if (!key.equalsIgnoreCase("Accept-Encoding")) { con.setRequestProperty(key, value); } } } this.con.connect(); break; // }catch( SSLException e ){ // // if ( i == 0 ){ // // if ( SESecurityManager.installServerCertificates( url ) != null ){ // // // certificate has been installed // // continue; // retry with new certificate // } // } // // throw( e ); // } catch (IOException e) { if (i == 0) { URL retry_url = UrlUtils.getIPV4Fallback(url); if (retry_url != null) { url = retry_url; } else { throw (e); } } } } int response = this.con.getResponseCode(); if (!ignoreReponseCode) { if ((response != HttpURLConnection.HTTP_ACCEPTED) && (response != HttpURLConnection.HTTP_OK)) { this.error(response, Integer.toString(response) + ": " + this.con.getResponseMessage()); return; } } /* Map headerFields = this.con.getHeaderFields(); System.out.println("Header of download of " + url_str); for (Iterator iter = headerFields.keySet().iterator(); iter.hasNext();) { String s = (String) iter.next(); System.out.println(s + ":" + headerFields.get(s)); } */ filename = this.con.getHeaderField("Content-Disposition"); if ((filename != null) && filename .toLowerCase() .matches(".*attachment.*")) // Some code to handle b0rked servers. while (filename.toLowerCase().charAt(0) != 'a') filename = filename.substring(1); if ((filename == null) || !filename.toLowerCase().startsWith("attachment") || (filename.indexOf('=') == -1)) { String tmp = this.url.getFile(); if (tmp.length() == 0 || tmp.equals("/")) { filename = url.getHost(); } else if (tmp.startsWith("?")) { // probably a magnet URI - use the hash // magnet:?xt=urn:sha1:VGC53ZWCUXUWVGX7LQPVZIYF4L6RXSU6 String query = tmp.toUpperCase(); int pos = query.indexOf("XT=URN:SHA1:"); if (pos == -1) { pos = query.indexOf("XT=URN:BTIH:"); } if (pos != -1) { pos += 12; int p2 = query.indexOf("&", pos); if (p2 == -1) { filename = query.substring(pos); } else { filename = query.substring(pos, p2); } } else { filename = "Torrent" + (long) (Math.random() * Long.MAX_VALUE); } filename += ".tmp"; } else { if (tmp.lastIndexOf('/') != -1) tmp = tmp.substring(tmp.lastIndexOf('/') + 1); // remove any params in the url int param_pos = tmp.indexOf('?'); if (param_pos != -1) { tmp = tmp.substring(0, param_pos); } filename = URLDecoder.decode(tmp, Constants.DEFAULT_ENCODING); } } else { filename = filename.substring(filename.indexOf('=') + 1); if (filename.startsWith("\"") && filename.endsWith("\"")) filename = filename.substring(1, filename.lastIndexOf('\"')); filename = URLDecoder.decode(filename, Constants.DEFAULT_ENCODING); // not sure of this piece of logic here but I'm not changing it at the moment File temp = new File(filename); filename = temp.getName(); } filename = FileUtil.convertOSSpecificChars(filename, false); // directoryname = // COConfigurationManager.getDirectoryParameter("General_sDefaultTorrent_Directory"); // boolean useTorrentSave = COConfigurationManager.getBooleanParameter("Save Torrent // Files"); directoryname = "D:\\Development\\testDownloads\\"; boolean useTorrentSave = true; if (file_str != null) { // not completely sure about the whole logic in this block File temp = new File(file_str); // if we're not using a default torrent save dir if (!useTorrentSave || directoryname.length() == 0) { // if it's already a dir if (temp.isDirectory()) { // use it directoryname = temp.getCanonicalPath(); } // it's a file else { // so use its parent dir directoryname = temp.getCanonicalFile().getParent(); } } // if it's a file if (!temp.isDirectory()) { // set the file name filename = temp.getName(); } } // what would happen here if directoryname == null and file_str == null?? this.state = STATE_INIT; this.notifyListener(); } catch (java.net.MalformedURLException e) { this.error(0, "Exception while parsing URL '" + url + "':" + e.getMessage()); } catch (java.net.UnknownHostException e) { this.error( 0, "Exception while initializing download of '" + url + "': Unknown Host '" + e.getMessage() + "'"); } catch (java.io.IOException ioe) { this.error(0, "I/O Exception while initializing download of '" + url + "':" + ioe.toString()); } catch (Throwable e) { this.error(0, "Exception while initializing download of '" + url + "':" + e.toString()); } if (this.state == STATE_ERROR) { return; } try { final boolean status_reader_run[] = {true}; this.state = STATE_START; notifyListener(); this.state = STATE_DOWNLOADING; notifyListener(); Thread status_reader = new AEThread("TorrentDownloader:statusreader") { public void runSupport() { boolean changed_status = false; while (true) { try { Thread.sleep(100); try { this_mon.enter(); if (!status_reader_run[0]) { break; } } finally { this_mon.exit(); } String s = con.getResponseMessage(); if (!s.equals(getStatus())) { if (!s.toLowerCase().startsWith("error:")) { if (s.toLowerCase().indexOf("alive") != -1) { if (percentDone < 10) { percentDone++; } } int pos = s.indexOf('%'); if (pos != -1) { int i; for (i = pos - 1; i >= 0; i--) { char c = s.charAt(i); if (!Character.isDigit(c) && c != ' ') { i++; break; } } try { percentDone = Integer.parseInt(s.substring(i, pos).trim()); } catch (Throwable e) { } } setStatus(s); } else { error(con.getResponseCode(), s.substring(6)); } changed_status = true; } } catch (Throwable e) { break; } } if (changed_status) { setStatus(""); } } }; status_reader.setDaemon(true); status_reader.start(); InputStream in; try { in = this.con.getInputStream(); } catch (FileNotFoundException e) { if (ignoreReponseCode) { in = this.con.getErrorStream(); } else { throw e; } } finally { try { this_mon.enter(); status_reader_run[0] = false; } finally { this_mon.exit(); } } // handle some servers that return gzip'd torrents even though we don't request it! String encoding = con.getHeaderField("content-encoding"); if (encoding != null) { if (encoding.equalsIgnoreCase("gzip")) { in = new GZIPInputStream(in); } else if (encoding.equalsIgnoreCase("deflate")) { in = new InflaterInputStream(in); } } if (this.state != STATE_ERROR) { this.file = new File(this.directoryname, filename); boolean useTempFile = false; try { this.file.createNewFile(); useTempFile = !this.file.exists(); } catch (Throwable t) { useTempFile = true; } if (useTempFile) { this.file = File.createTempFile("AZU", ".torrent", new File(this.directoryname)); this.file.createNewFile(); } FileOutputStream fileout = new FileOutputStream(this.file, false); bufBytes = 0; int size = (int) UrlUtils.getContentLength(con); this.percentDone = -1; do { if (this.cancel) { break; } try { bufBytes = in.read(buf); this.readTotal += bufBytes; if (size != 0) { this.percentDone = (100 * this.readTotal) / size; } notifyListener(); } catch (IOException e) { } if (bufBytes > 0) { fileout.write(buf, 0, bufBytes); } } while (bufBytes > 0); in.close(); fileout.flush(); fileout.close(); if (this.cancel) { this.state = STATE_CANCELLED; if (deleteFileOnCancel) { this.cleanUpFile(); } } else { if (this.readTotal <= 0) { this.error(0, "No data contained in '" + this.url.toString() + "'"); return; } // if the file has come down with a not-so-useful name then we try to rename // it to something more useful try { if (!filename.toLowerCase().endsWith(".torrent")) { TOTorrent torrent = TorrentUtils.readFromFile(file, false); String name = TorrentUtils.getLocalisedName(torrent) + ".torrent"; File new_file = new File(directoryname, name); if (file.renameTo(new_file)) { filename = name; file = new_file; } } } catch (Throwable e) { Debug.printStackTrace(e); } // TorrentUtils.setObtainedFrom( file, original_url ); this.state = STATE_FINISHED; } this.notifyListener(); } } catch (Exception e) { if (!cancel) { Debug.out("'" + this.directoryname + "' '" + filename + "'", e); } this.error(0, "Exception while downloading '" + this.url.toString() + "':" + e.getMessage()); } }
public Socket createSocket(String host, int port) throws IOException, FingerprintException, FingerprintError { Socket s = new Socket(); s.connect(new InetSocketAddress(proxyHost, proxyPort), 1000 * timeout); PrintWriter out = new PrintWriter(s.getOutputStream()); InputStreamReader isr = new InputStreamReader(s.getInputStream()); BufferedReader in = new BufferedReader(isr); String[] connHdr = { "CONNECT " + host + ":" + port + " HTTP/1.1", "Host: " + host + ":" + port, "Proxy-Connection: close" }; String[] connReq = null; if (authRequired) { connReq = new String[connHdr.length + 1]; for (int i = 0; i < connHdr.length; i++) { connReq[i] = connHdr[i]; } connReq[connHdr.length] = "Proxy-Authorization: " + authenticator; } else { connReq = new String[connHdr.length]; for (int i = 0; i < connHdr.length; i++) { connReq[i] = connHdr[i]; } } sendRequest(out, connReq); HttpResponseHeader hdr = new HttpResponseHeader(in); int status = hdr.getStatusCode(); if (Debug.get(Debug.Communication)) { System.err.println("Got status " + status); } /* Now check the status code and act upon it */ if ((status / 100) == 2) { // Code is 2xx return s; } if (((status / 100) == 4) && (status != 407)) { throw new HttpProxyError("Proxy request failed"); } if ((status == 407) && authRequired) { /* We already sent authorisation info, * but the proxy did not accept it. * So we can only stop here, as * we do not have the right credentials * at hand. */ throw new HttpProxyError("Cannot authenticate " + "to proxy, wrong " + "credentials?"); } if (status == 407) { if (Debug.get(Debug.SockInit)) { System.err.println("Preparing proxy " + "authenticator for " + hdr.getProxyAuthInfo()); } prepareAuthorisation(hdr.getProxyAuthInfo()); authRequired = true; return createSocket(host, port); } return s; }
/* * Calculate the keys needed for this connection, once the session's * master secret has been calculated. Uses the master key and nonces; * the amount of keying material generated is a function of the cipher * suite that's been negotiated. * * This gets called both on the "full handshake" (where we exchanged * a premaster secret and started a new session) as well as on the * "fast handshake" (where we just resumed a pre-existing session). */ void calculateConnectionKeys(SecretKey masterKey) { /* * For both the read and write sides of the protocol, we use the * master to generate MAC secrets and cipher keying material. Block * ciphers need initialization vectors, which we also generate. * * First we figure out how much keying material is needed. */ int hashSize = cipherSuite.macAlg.size; boolean is_exportable = cipherSuite.exportable; BulkCipher cipher = cipherSuite.cipher; int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; // Which algs/params do we need to use? String keyMaterialAlg; PRF prf; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { keyMaterialAlg = "SunTls12KeyMaterial"; prf = cipherSuite.prfAlg; } else { keyMaterialAlg = "SunTlsKeyMaterial"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( masterKey, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, cipher.algorithm, cipher.keySize, expandedKeySize, cipher.ivSize, hashSize, prfHashAlg, prfHashLength, prfBlockSize); try { KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); kg.init(spec); TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec) kg.generateKey(); clntWriteKey = keySpec.getClientCipherKey(); svrWriteKey = keySpec.getServerCipherKey(); // Return null if IVs are not supposed to be generated. // e.g. TLS 1.1+. clntWriteIV = keySpec.getClientIv(); svrWriteIV = keySpec.getServerIv(); clntMacSecret = keySpec.getClientMacKey(); svrMacSecret = keySpec.getServerMacKey(); } catch (GeneralSecurityException e) { throw new ProviderException(e); } // // Dump the connection keys as they're generated. // if (debug != null && Debug.isOn("keygen")) { synchronized (System.out) { HexDumpEncoder dump = new HexDumpEncoder(); System.out.println("CONNECTION KEYGEN:"); // Inputs: System.out.println("Client Nonce:"); printHex(dump, clnt_random.random_bytes); System.out.println("Server Nonce:"); printHex(dump, svr_random.random_bytes); System.out.println("Master Secret:"); printHex(dump, masterKey.getEncoded()); // Outputs: System.out.println("Client MAC write Secret:"); printHex(dump, clntMacSecret.getEncoded()); System.out.println("Server MAC write Secret:"); printHex(dump, svrMacSecret.getEncoded()); if (clntWriteKey != null) { System.out.println("Client write key:"); printHex(dump, clntWriteKey.getEncoded()); System.out.println("Server write key:"); printHex(dump, svrWriteKey.getEncoded()); } else { System.out.println("... no encryption keys used"); } if (clntWriteIV != null) { System.out.println("Client write IV:"); printHex(dump, clntWriteIV.getIV()); System.out.println("Server write IV:"); printHex(dump, svrWriteIV.getIV()); } else { if (protocolVersion.v >= ProtocolVersion.TLS11.v) { System.out.println("... no IV derived for this protocol"); } else { System.out.println("... no IV used for this cipher"); } } System.out.flush(); } } }
/* * Calculate the master secret from its various components. This is * used for key exchange by all cipher suites. * * The master secret is the catenation of three MD5 hashes, each * consisting of the pre-master secret and a SHA1 hash. Those three * SHA1 hashes are of (different) constant strings, the pre-master * secret, and the nonces provided by the client and the server. */ private SecretKey calculateMasterSecret( SecretKey preMasterSecret, ProtocolVersion requestedVersion) { if (debug != null && Debug.isOn("keygen")) { HexDumpEncoder dump = new HexDumpEncoder(); System.out.println("SESSION KEYGEN:"); System.out.println("PreMaster Secret:"); printHex(dump, preMasterSecret.getEncoded()); // Nonces are dumped with connection keygen, no // benefit to doing it twice } // What algs/params do we need to use? String masterAlg; PRF prf; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { masterAlg = "SunTls12MasterSecret"; prf = cipherSuite.prfAlg; } else { masterAlg = "SunTlsMasterSecret"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( preMasterSecret, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, prfHashAlg, prfHashLength, prfBlockSize); SecretKey masterSecret; try { KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); kg.init(spec); masterSecret = kg.generateKey(); } catch (GeneralSecurityException e) { // For RSA premaster secrets, do not signal a protocol error // due to the Bleichenbacher attack. See comments further down. if (!preMasterSecret.getAlgorithm().equals("TlsRsaPremasterSecret")) { throw new ProviderException(e); } if (debug != null && Debug.isOn("handshake")) { System.out.println("RSA master secret generation error:"); e.printStackTrace(System.out); System.out.println("Generating new random premaster secret"); } if (requestedVersion != null) { preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion); } else { preMasterSecret = RSAClientKeyExchange.generateDummySecret(protocolVersion); } // recursive call with new premaster secret return calculateMasterSecret(preMasterSecret, null); } // if no version check requested (client side handshake), or version // information is not available (not an RSA premaster secret), // return master secret immediately. if ((requestedVersion == null) || !(masterSecret instanceof TlsMasterSecret)) { return masterSecret; } // we have checked the ClientKeyExchange message when reading TLS // record, the following check is necessary to ensure that // JCE provider does not ignore the checking, or the previous // checking process bypassed the premaster secret version checking. TlsMasterSecret tlsKey = (TlsMasterSecret) masterSecret; int major = tlsKey.getMajorVersion(); int minor = tlsKey.getMinorVersion(); if ((major < 0) || (minor < 0)) { return masterSecret; } // check if the premaster secret version is ok // the specification says that it must be the maximum version supported // by the client from its ClientHello message. However, many // implementations send the negotiated version, so accept both // for SSL v3.0 and TLS v1.0. // NOTE that we may be comparing two unsupported version numbers, which // is why we cannot use object reference equality in this special case. ProtocolVersion premasterVersion = ProtocolVersion.valueOf(major, minor); boolean versionMismatch = (premasterVersion.v != requestedVersion.v); /* * we never checked the client_version in server side * for TLS v1.0 and SSL v3.0. For compatibility, we * maintain this behavior. */ if (versionMismatch && requestedVersion.v <= ProtocolVersion.TLS10.v) { versionMismatch = (premasterVersion.v != protocolVersion.v); } if (versionMismatch == false) { // check passed, return key return masterSecret; } // Due to the Bleichenbacher attack, do not signal a protocol error. // Generate a random premaster secret and continue with the handshake, // which will fail when verifying the finished messages. // For more information, see comments in PreMasterSecret. if (debug != null && Debug.isOn("handshake")) { System.out.println( "RSA PreMasterSecret version error: expected" + protocolVersion + " or " + requestedVersion + ", decrypted: " + premasterVersion); System.out.println("Generating new random premaster secret"); } preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion); // recursive call with new premaster secret return calculateMasterSecret(preMasterSecret, null); }