⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcpprotocoldecoderphe.java

📁 基于JXTA开发平台的下载软件开发源代码
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
 * Created on 17-Jan-2006
 * Created by Paul Gardner
 * Copyright (C) 2006 Aelitis, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * AELITIS, SAS au capital de 46,603.30 euros
 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
 *
 */

package com.aelitis.azureus.core.networkmanager.impl;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.*;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.logging.LogAlert;
import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.SHA1Hasher;
import org.gudy.azureus2.core3.util.SystemTime;

import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.networkmanager.VirtualChannelSelector;
import com.aelitis.azureus.core.networkmanager.VirtualChannelSelector.VirtualSelectorListener;
import com.aelitis.azureus.core.util.bloom.BloomFilter;
import com.aelitis.azureus.core.util.bloom.BloomFilterFactory;

public class 
TCPProtocolDecoderPHE 
	extends TCPProtocolDecoder 
	implements VirtualSelectorListener
{
	private static final LogIDs LOGID = LogIDs.NWMAN;

	private static final byte		CRYPTO_PLAIN	= 0x01;
	private static final byte		CRYPTO_RC4		= 0x02;
	private static final byte		CRYPTO_XOR		= 0x04;
	private static final byte		CRYPTO_AES		= 0x08;

	
	//private static final String 	DH_P = "92d862b3a95bff4e6cbdce3a266ff4b46e6e1ecad76c0a877d92a3dae4999e6414efde56fc14d1cca6d5408a8ef9ea248389168876b6e8f4503845dfe373549f";
	//private static final String 	DH_G = "4383b53ee650fd73e41e8c9e8527997ab8cb41e1cbd73ac7685493e1e5d091e3e3789dea03ab9d5b2c368faa617bb30e427cbaeb23c268edb38eb8c747756080";
	// private static final String 	DH_P = "f3f90c790c63b119f9c1be43fdb12dc6ed6f26325999c01ba6ed373e75d6b2dee8d1c0475652a987c8df57b23d395bdb142be316d780b9361f85629535030873";
	
	private static final String 	DH_P = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563";
	private static final String 	DH_G = "02";
	private static final int		DH_L = 160;
	
	private static final int		DH_SIZE_BYTES	= DH_P.length()/2;

	private static final BigInteger	DH_P_BI = new BigInteger( DH_P, 16 );
	private static final BigInteger	DH_G_BI = new BigInteger( DH_G, 16 );
	
	private static KeyPairGenerator		dh_key_generator;
	private static long					last_dh_incoming_key_generate;
	
	private static final int			BLOOM_RECREATE				= 30*1000;
	private static final int			BLOOM_INCREASE				= 1000;
	private static BloomFilter			generate_bloom				= BloomFilterFactory.createAddRemove4Bit(BLOOM_INCREASE);
	private static long					generate_bloom_create_time	= SystemTime.getCurrentTime();
	
	private static boolean	crypto_ok;
	//private static boolean	aes_ok;
	
	/*
	private static final String		AES_STREAM_ALG				= "AES";
	private static final String		AES_STREAM_CIPHER			= "AES/CFB8/NoPadding";
	private static final int		AES_STREAM_KEY_SIZE			= 128;
	private static final int		AES_STREAM_KEY_SIZE_BYTES	= AES_STREAM_KEY_SIZE/8;
   	*/
	
	//private static final byte[]		AES_STREAM_IV				= 
    //	{ 	(byte)0x15, (byte)0xE0, (byte)0x6B, (byte)0x7E, (byte)0x98, (byte)0x59, (byte)0xE4, (byte)0xA7, 
    //		(byte)0x34, (byte)0x66, (byte)0xAD, (byte)0x48, (byte)0x35, (byte)0xE2, (byte)0xD0, (byte)0x24 };
    
    
	private static final String		RC4_STREAM_ALG				= "RC4";
	private static final String		RC4_STREAM_CIPHER			= "RC4";
	private static final int		RC4_STREAM_KEY_SIZE			= 128;
	private static final int		RC4_STREAM_KEY_SIZE_BYTES	= RC4_STREAM_KEY_SIZE/8;
    
    
    private static final int		PADDING_MAX	= 512;
    
	private static final Random	random = new SecureRandom();
	
	private static Map	shared_secrets	= new HashMap();
	
	static{
		try{
			DHParameterSpec dh_param_spec = new DHParameterSpec( DH_P_BI, DH_G_BI, DH_L );
			
			dh_key_generator = KeyPairGenerator.getInstance("DH");
	        
			dh_key_generator.initialize(dh_param_spec);
	        
			dh_key_generator.generateKeyPair();
	               	
		    byte[]	rc4_test_secret = new byte[RC4_STREAM_KEY_SIZE_BYTES];

		    SecretKeySpec	rc4_test_secret_key_spec = new SecretKeySpec(rc4_test_secret, 0, RC4_STREAM_KEY_SIZE_BYTES, RC4_STREAM_ALG );
		        		        
		    TCPTransportCipher rc4_cipher = new TCPTransportCipher( RC4_STREAM_CIPHER, Cipher.ENCRYPT_MODE, rc4_test_secret_key_spec );
		         
		    rc4_cipher = new TCPTransportCipher( RC4_STREAM_CIPHER, Cipher.DECRYPT_MODE, rc4_test_secret_key_spec );
	        
		    /*
			try{
				byte[]	aes_test_secret = new byte[AES_STREAM_KEY_SIZE_BYTES];
	        	 
				SecretKeySpec	aes_test_secret_key_spec = new SecretKeySpec(aes_test_secret, 0, AES_STREAM_KEY_SIZE_BYTES, AES_STREAM_ALG );
		        	        
				AlgorithmParameterSpec	spec = 	new IvParameterSpec( aes_test_secret );
		        
		        TCPTransportCipher aes_cipher = new TCPTransportCipher( AES_STREAM_CIPHER, Cipher.ENCRYPT_MODE, aes_test_secret_key_spec, spec );
		        
		        aes_cipher = new TCPTransportCipher( AES_STREAM_CIPHER, Cipher.DECRYPT_MODE, aes_test_secret_key_spec, spec );
		        
		        aes_ok	= true;
		        
			}catch( Throwable e ){
				
				Logger.log(	new LogEvent(LOGID, "AES Unavailable", e ));
			}
	        */
		    
	        crypto_ok	= true;
	        
	     	if (Logger.isEnabled()){
	     		
        		Logger.log(	new LogEvent(LOGID, "PHE crypto initialised" ));
	     	}
		}catch( NoClassDefFoundError e ){
			
				// running without PHE classes, not such a severe error
      	
			Logger.log(	new LogEvent(LOGID, "PHE crypto disabled as classes unavailable" ));
			
			crypto_ok	= false;
			
		}catch( Throwable e ){
				     		
        	Logger.log(	new LogEvent(LOGID, "PHE crypto initialisation failed", e ));
			
			crypto_ok	= false;
		}
	}
	
	public static boolean
	isCryptoOK()
	{
		return( crypto_ok );
	}
	
	public static void
	addSecretSupport(
		byte[]		secret )
	{
		SHA1Hasher hasher = new SHA1Hasher();
   		
   		hasher.update( REQ2_IV );
   		hasher.update( secret );
   		
   		byte[]	encoded = hasher.getDigest();
		                  	
		synchronized( shared_secrets ){
			
			shared_secrets.put( new HashWrapper( encoded ), secret );
		}
	}
	
	public static void
	removeSecretSupport(
		byte[]		secret )
	{
		SHA1Hasher hasher = new SHA1Hasher();
   		
   		hasher.update( REQ2_IV );
   		hasher.update( secret );
   		
   		byte[]	encoded = hasher.getDigest();
		                  	
		synchronized( shared_secrets ){
			
			shared_secrets.remove( new HashWrapper( encoded ));
		}
	}
	
	// private static final byte SUPPORTED_PROTOCOLS = (byte)((aes_ok?CRYPTO_AES:0) | CRYPTO_RC4 | CRYPTO_XOR | CRYPTO_PLAIN );
	private static final byte SUPPORTED_PROTOCOLS = (byte)(CRYPTO_RC4 | CRYPTO_PLAIN );

	
	private static byte 	MIN_CRYPTO;
	
	static{
	    COConfigurationManager.addAndFireParameterListeners(
	    		new String[]{ "network.transport.encrypted.min_level" },
	    		new ParameterListener()
	    		{
	    			 public void 
	    			 parameterChanged(
	    				String ignore )
	    			 {
	    				 if ( NetworkManager.REQUIRE_CRYPTO_HANDSHAKE && !isCryptoOK() ){	    					 
	    					 Logger.log( new LogAlert(true,LogAlert.AT_ERROR,"Connection encryption unavailable, please update your Java version" ));
	    				 }
	    				 
	    				 String	min	= COConfigurationManager.getStringParameter( "network.transport.encrypted.min_level");
	    				 
	    				 if ( min.equals( "XOR" )){
	    					 
	    					 MIN_CRYPTO	= CRYPTO_XOR | CRYPTO_RC4 | CRYPTO_AES;
	    					 
	    				 }else if ( min.equals( "RC4" )){
	    					 
	    					 MIN_CRYPTO	= CRYPTO_RC4 | CRYPTO_AES;
	    					 
	    				 }else if ( min.equals( "AES" )){
	    					
	    					 MIN_CRYPTO	= CRYPTO_AES;
	    					 
	    				 }else{
	    					 
	    					 MIN_CRYPTO	= CRYPTO_PLAIN | CRYPTO_XOR | CRYPTO_RC4 | CRYPTO_AES;
	    				 } 
	    				 
	    				 MIN_CRYPTO = (byte)(MIN_CRYPTO & SUPPORTED_PROTOCOLS);
	    			 }
	    		});
	}
	
		
	private static VirtualChannelSelector	read_selector	= NetworkManager.getSingleton().getReadSelector();
	private static VirtualChannelSelector	write_selector	= NetworkManager.getSingleton().getWriteSelector();

	private static final int		PS_OUTBOUND_1	= 0;
	private static final int		PS_OUTBOUND_2	= 1;
	private static final int		PS_OUTBOUND_3	= 2;
	private static final int		PS_OUTBOUND_4	= 3;
	
	private static final int		PS_INBOUND_1	= 10;
	private static final int		PS_INBOUND_2	= 11;
	private static final int		PS_INBOUND_3	= 12;
	private static final int		PS_INBOUND_4	= 13;

	public static final byte[]	KEYA_IV	= "keyA".getBytes();
	public static final byte[]	KEYB_IV	= "keyB".getBytes();
	public static final byte[]	REQ1_IV	= "req1".getBytes();
	public static final byte[]	REQ2_IV	= "req2".getBytes();
	public static final byte[]	REQ3_IV	= "req3".getBytes();
	public static final byte[]	VC		= { 0,0,0,0,0,0,0,0};
	
	
	
	private SocketChannel		channel;
	private ByteBuffer			write_buffer;
	private ByteBuffer			read_buffer;
	

	private TCPProtocolDecoderAdapter	adapter;
	
	private KeyAgreement 	key_agreement;
	private byte[]			dh_public_key_bytes;
	
	private byte[]			shared_secret;
	private byte[]			secret_bytes;
	
	private static final int	OUTBOUND_IA	= 0;
	
	private int				initial_data_out_len;
	private int				initial_data_in_len;
	
	private TCPTransportCipher		write_cipher;
	private TCPTransportCipher		read_cipher;

	private byte[]			padding_skip_marker;
	
	private byte			my_supported_protocols;
	private byte			selected_protocol;
		
	private boolean	outbound;
	
	private int		protocol_state;
	private int		protocol_substate;
	
	private boolean	handshake_complete;

	private int		bytes_read;
	private int		bytes_written;
	
	private long	last_read_time	= SystemTime.getCurrentTime();
	
	private TCPTransportHelperFilter		filter;
	
	private boolean processing_complete;
	
	private AEMonitor	process_mon	= new AEMonitor( "TCPProtocolDecoder:process" );
	
	public 
	TCPProtocolDecoderPHE(
		SocketChannel				_channel,
		byte[]						_shared_secret,
		ByteBuffer					_header,
		TCPProtocolDecoderAdapter	_adapter )
	
		throws IOException
	{
		super( false );
		
		if ( !isCryptoOK()){
			
			throw( new IOException( "PHE crypto broken" ));
		}
		
		channel			= _channel;
		shared_secret	= _shared_secret;
		adapter			= _adapter;
		
		if ( shared_secret == null ){
			
			shared_secret	= new byte[0];
		}
		
		outbound	= _header == null;
		
		if ( outbound ){
			
			initial_data_out_len	= OUTBOUND_IA;
		}
		
		my_supported_protocols = SUPPORTED_PROTOCOLS;
				
		if ( outbound ){
			
			//if ( !NetworkManager.REQUIRE_CRYPTO_HANDSHAKE ){				
			//	throw( new IOException( "Crypto encoder selected for outbound but crypto not required" ));
			//}
			
				// outbound connection, we require a certain minimal level of support
			
			my_supported_protocols = MIN_CRYPTO;
			
		}else{
			
				// incoming. If we require crypto then we use minimum otherwise available
			
			if ( NetworkManager.REQUIRE_CRYPTO_HANDSHAKE ){
				
				my_supported_protocols = MIN_CRYPTO;
			}
		}
		
		initCrypto();

		read_selector.register( channel, this, null );
		write_selector.register( channel, this, null );
		
		write_selector.pauseSelects( channel );
		
		if ( outbound ){
		
			protocol_state	= PS_OUTBOUND_1;

			read_selector.pauseSelects( channel );
			
		}else{
			
			protocol_state	= PS_INBOUND_1;

			read_buffer = ByteBuffer.allocate( dh_public_key_bytes.length );					
				
			read_buffer.put( _header );
		
			bytes_read += _header.limit();
		}
		
		process();
	}
	
	protected void
	initCrypto()
	
		throws IOException
	{
		try{
	        KeyPair key_pair = generateDHKeyPair( channel, outbound );
	    	    
	        key_agreement = KeyAgreement.getInstance("DH");
	        
	        key_agreement.init(key_pair.getPrivate());
	       
	        DHPublicKey	dh_public_key = (DHPublicKey)key_pair.getPublic();
	        
	        BigInteger	dh_y = dh_public_key.getY();
	        
	        dh_public_key_bytes = bigIntegerToBytes( dh_y, DH_SIZE_BYTES );
	        
		}catch( Throwable e ){
			
			throw( new IOException( Debug.getNestedExceptionMessage(e)));
		}
	}
	
	protected void
	completeDH(
		byte[]	buffer )
	
		throws IOException
	{
		try{			
	        BigInteger	other_dh_y = bytesToBigInteger( buffer, 0, DH_SIZE_BYTES );
	        
	        KeyFactory dh_key_factory = KeyFactory.getInstance("DH");
	        	    
		    PublicKey other_public_key = dh_key_factory.generatePublic( new DHPublicKeySpec( other_dh_y, DH_P_BI, DH_G_BI ));
	        		
		    key_agreement.doPhase( other_public_key, true );
		    
		    secret_bytes = key_agreement.generateSecret();
			    
		    // System.out.println( "secret = " + ByteFormatter.encodeString( secret_bytes ));
		    
		}catch( Throwable e ){
			
			throw( new IOException( Debug.getNestedExceptionMessage(e)));
		}
	}
	
	protected void
	setupCrypto()
	
		throws IOException
	{
		try{
		    //"HASH('keyA', S, SKEY)" if you're A
		    //"HASH('keyB', S, SKEY)" if you're B

		    SHA1Hasher	hasher = new SHA1Hasher();
		    

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -