JAVA:使用CipherInputStream和CipherOutputStream的套接字覆盖

时间:2014-08-29 15:27:24

标签: java sockets encryption aes

让我解释一下我的问题 我想覆盖Socket和ServerSocket类,以便以这种方式加密我的消息:
1)客户端将随机生成的对称密钥(AES算法)发送到服务器
2)之后,客户端和服务器可以通过使用此密钥加密其消息进行通信 3)为了交换对称密钥,客户端使用服务器的公钥(RSA算法)对其进行加密

我覆盖Socket和ServerSocket,因此当客户端打开Socket时,它会自动发送由服务器公钥加密的对称密钥。服务器读取流中的前128个字节,对其进行解码,并构建对称密钥。 这部分似乎有效。我使用Wireshark检查通信:数据包是加密的,接收的对称密钥是否正确传递。 为了保证透明地使用我的套接字,我重写 getInputStream getOutputStream 方法,返回 CipheredInputStream ChiperedOutputStream

它现在不起作用。当我尝试让OutputStream发送数据时,程序会通过指令,但它并不重要(我通过Wireshark检查并且没有发送数据包)。

这是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;
    }
}

这是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);
    }

}

我无法弄清问题在哪里。谢谢!

1 个答案:

答案 0 :(得分:0)

问题是密码需要特定数量的数据(例如128位)才能正确加密。如果您发送文件,那没问题,因为关闭流时将发送文件的最后几位。

但是,您需要填充进行网络通信。您可以为Cipher实例指定一个:

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

使用填充,一旦调用flush()方法,Cipher将能够发送您的数据(无论何时您都希望发送某些内容,您应该这样做。)

注意:如果您的客户使用公钥分发,您的应用程序将是安全的。否则,您无法确定是否首先连接到正确的服务器。任何人都可以创建公钥。