简体   繁体   English

JAVA:套接字重写以使用CipherInputStream和CipherOutputStream

[英]JAVA: Socket override to use CipherInputStream and CipherOutputStream

Let me explain my problem. 让我解释一下我的问题。
I want to override Socket and ServerSocket classes in order to encrypt my messages in this way: 我想重写Socket和ServerSocket类,以便以此方式加密我的消息:
1) Client sends a random generated symmetric key (AES algorithm) to the Server 1)客户端向服务器发送随机生成的对称密钥(AES算法)
2) After that, client and server can communicate by encrypting their messages with this key 2)之后,客户端和服务器可以通过使用此密钥加密其消息来进行通信
3) To exchange the symmetric key the client encrypts it using the public key of the server (RSA algorithm) 3)要交换对称密钥,客户端使用服务器的公共密钥对其进行加密(RSA算法)

I override Socket and ServerSocket, so automatically, when the client opens a Socket, this will send the symmetric key encrypted by the server's public key. 我重写了Socket和ServerSocket,所以当客户端打开一个Socket时,它将自动发送由服务器的公共密钥加密的对称密钥。 The server reads the first 128 byte in the stream, decodes them, and builds the symmetric key. 服务器读取流中的前128个字节,对其进行解码,然后构建对称密钥。 This part seems work. 这部分似乎工作。 I check the communication using Wireshark: packets are encrypted and received symmetric key is correctly delivered. 我使用Wireshark检查通信:数据包已加密,并且接收到的对称密钥已正确传递。 In order to guarantee a transparent use of my Sockets I override the getInputStream and getOutputStream methods, returning a CipheredInputStream and a ChiperedOutputStream . 为了保证透明地使用我的套接字我重写的getInputStreamgetOutputStream方法方法,返回CipheredInputStreamChiperedOutputStream。

It doesn't work for now.. When I try to get OutputStream to send data, the program goes through the instruction but it doesn't matter (I check via Wireshark and no packets are sent). 暂时不起作用。当我尝试让OutputStream发送数据时,程序会通过指令进行操作,但这没有关系(我通过Wireshark进行了检查,未发送任何数据包)。

This is the code of the ServerSocket: 这是ServerSocket的代码:

public class SecureServerSocket extends ServerSocket {

    public SecureServerSocket(int port) throws IOException {
        super(port);
    }

    public Socket accept() throws IOException {
        SecureSocket s = new SecureSocket();
        implAccept(s);

        SecretKey seckey;
        InputStream is = s.getInputStream();

        byte[] tmp = new byte[128]; //128: length of the key
        int i = 0;
        while (i < 128) {
            tmp[i] = (byte) (is.read() & 0x000000FF);
            ++i;
        }

        byte[] mess = EncryptionManager.rsaDecryptPrivate(tmp);

        seckey = new SecretKeySpec(mess, "AES");

        try {
            s.setkey(seckey);
        } catch (InvalidKeyException | NoSuchAlgorithmException
                | NoSuchPaddingException e) {
            e.printStackTrace();
        }
        return s;
    }
}

This is the code of the Socket: 这是套接字的代码:

public class SecureSocket extends Socket {

    private SecretKey seckey;

    private InputStream in = null;
    private OutputStream out = null;
    private CipherInputStream cin = null;
    private CipherOutputStream cout = null;

    public SecureSocket() throws IOException {
    }

    public SecureSocket(String address, int port) throws UnknownHostException,
            IOException, NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException {

        super(address, port);

        if (out == null) {
            this.out = super.getOutputStream();
        }

        if (in == null) {
            this.in = super.getInputStream();
        }

        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        SecureRandom random = new SecureRandom();
        keyGen.init(random);
        seckey = keyGen.generateKey();

        byte[] mess = EncryptionManager.rsaEncryptPublic(seckey.getEncoded());

        // writing the initial message with the AES encryption key
        out.write(mess);

        // Initialization of the Cipher streams
        Cipher cipherEn = Cipher.getInstance("AES");
        cipherEn.init(Cipher.ENCRYPT_MODE, seckey);
        Cipher cipherDc = Cipher.getInstance("AES");
        cipherDc.init(Cipher.DECRYPT_MODE, seckey);

        cout = new CipherOutputStream(out, cipherEn);
        cin = new CipherInputStream(in, cipherDc);

    }

    public InputStream getInputStream() throws IOException {
        if (cin == null)
            return super.getInputStream();
        return cin;
    }

    public OutputStream getOutputStream() throws IOException {
        if (cout == null)
            return super.getOutputStream();
        return cout;
    }

    public synchronized void close() throws IOException {
        OutputStream o = getOutputStream();
        o.flush();
        super.close();
    }

    public void setkey(SecretKey seckey) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IOException {

        this.seckey = seckey;

        Cipher cipherEn = Cipher.getInstance("AES");
        cipherEn.init(Cipher.ENCRYPT_MODE, seckey);

        cout = new CipherOutputStream(super.getOutputStream(), cipherEn);

        Cipher cipherDc = Cipher.getInstance("AES");
        cipherDc.init(Cipher.DECRYPT_MODE, seckey);

        cin = new CipherInputStream(super.getInputStream(), cipherDc);
    }

}

I can't figure out where is the problem. 我不知道问题出在哪里。 Thank you! 谢谢!

The problem is that a Cipher requires a specific amount of data (for example 128 bits) to be able to encrypt it correctly. 问题是密码需要一定数量的数据(例如128位)才能正确加密。 If you send a file, that's no problem because the last few bits of the file will be sent when you close the stream. 如果发送文件,那没问题,因为关闭流时将发送文件的最后几位。

However, you need a padding for network communication. 但是,您需要用于网络通信的填充 You can specify one for your Cipher instances: 您可以为您的Cipher实例指定一个:

Cipher cipherEnOrDe = Cipher.getInstance("AES/CBC/PKCS5Padding"); //for example, check documentation for more

Using a padding, the Cipher will be able to send your data once you call the flush() method (which you should do whenever you want something to be sent anyway). 使用填充,一旦调用flush()方法,Cipher将能够发送数据(无论何时要发送某些内容,都应执行此操作)。

Note: Your application is only safe if your client is distributed with the public key. 注意:仅当使用公共密钥分发客户端时,您的应用程序才是安全的。 Otherwise, you cannot be sure that you are connecting to the right server in the first place. 否则,您不能确保首先连接到正确的服务器。 Anyone can create a public key. 任何人都可以创建一个公钥。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM