ObjectInputStream 错误:StreamCorruptedException

问题描述

我正在制作一个即时消息应用程序,在其中我在两部手机之间建立了 TLS 连接,其中一部作为服务器,另一部作为客户端。尝试发送消息时,我在客户端收到此异常,其中服务器尝试在行 inputStream = new ObjectInputStream (sslSocket.getInputStream());

上发送消息

有人知道问题出在哪里吗?

    2021-04-07 17:35:58.528 2925-6197/com.example.testaware W/ample.testawar: Long monitor contention with owner Thread-7 (6196) at int com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(byte[],int,int)(ConscryptFileDescriptorSocket.java:572) waiters=0 in int com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(byte[],int) for 11.871s
2021-04-07 17:35:58.531 2925-6196/com.example.testaware W/System.err: java.io.StreamCorruptedException: invalid stream header: ACED7372
2021-04-07 17:35:58.531 2925-6196/com.example.testaware W/System.err:     at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:862)
2021-04-07 17:35:58.532 2925-6196/com.example.testaware W/System.err:     at java.io.ObjectInputStream.<init>(ObjectInputStream.java:353)
2021-04-07 17:35:58.532 2925-6196/com.example.testaware W/System.err:     at com.example.testaware.AppClient.run(AppClient.java:187)
2021-04-07 17:35:58.532 2925-6196/com.example.testaware W/System.err:     at java.lang.Thread.run(Thread.java:919)
2021-04-07 17:35:58.532 2925-6196/com.example.testaware D/LOG-Test-Aware-Client: Exception in Appclient  in run()
2021-04-07 17:35:58.532 2925-6197/com.example.testaware W/System.err: java.io.StreamCorruptedException: invalid stream header: 0005002A
2021-04-07 17:35:58.533 2925-6197/com.example.testaware W/System.err:     at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:862)
2021-04-07 17:35:58.533 2925-6197/com.example.testaware W/System.err:     at java.io.ObjectInputStream.<init>(ObjectInputStream.java:353)
2021-04-07 17:35:58.533 2925-6197/com.example.testaware W/System.err:     at com.example.testaware.AppClient.run(AppClient.java:187)
2021-04-07 17:35:58.534 2925-6197/com.example.testaware W/System.err:     at java.lang.Thread.run(Thread.java:919)

AppClient 类:

public class AppClient implements Runnable{

    private boolean running;
    @Getter
    private SSLSocket sslSocket;
    private SSLContext sslContext;

    private ObjectInputStream inputStream;
    private ObjectOutputStream outputStream;
    private ExecutorService sendService = Executors.newSingleThreadExecutor();

    @Getter
    private List<ConnectionListener> connectionListeners;

    private String LOG = "LOG-Test-Aware-Client";
    @Getter
    private KeyPair keyPair;

    private Inet6Address inet6Address;

    private int port;

    @Getter
    private MessageReceivedobserver messageReceivedobserver;

    public AppClient(KeyPair keyPair,SSLContext sslContext){
        this.keyPair = keyPair;
        this.sslContext = sslContext;
        this.inet6Address = MainActivity.getPeerIpv6();

        connectionListeners = new ArrayList<>();
    }


    private X509Certificate getServerIdentity() {
        try {
            Certificate[] certs = sslSocket.getSession().getPeerCertificates();
            if(certs.length > 0 && certs[0] instanceof X509Certificate) {
                return (X509Certificate) certs[0];
            }
        } catch (SSLPeerUnverifiedException | NullPointerException ignored) {
            ignored.printstacktrace();

        }
        return null;
    }

  
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public boolean sendMessage(Message message){
        if(outputStream == null){
            Log.d(LOG,"outputstream is null");
            return false;
        }
        Runnable sendMessageRunnable = () -> {
            try {
                MessagePacket messagePacket = (new MessagePacket(message));
                Log.d(LOG,"outputstream " + message);
                outputStream.writeObject(messagePacket);
                outputStream.flush();
            } catch (IOException e) {
                e.printstacktrace();
                Log.d(LOG,"Exception in Appclient  in sendMessage()");
                running = false;
            }
        };
        sendService.submit(sendMessageRunnable);
        return true;
    }


    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    public void run() {
        running = true;
        sslSocket = null;

        this.port = Constants.SERVER_PORT;

        SSLSocketFactory socketFactory = sslContext.getSocketFactory();
        try {
            while(running){
                sslSocket = (SSLSocket) socketFactory.createSocket(inet6Address,Constants.SERVER_PORT);


                outputStream = new ObjectOutputStream(sslSocket.getoutputStream());
                inputStream = new ObjectInputStream (sslSocket.getInputStream());
                outputStream.writeUTF("clientHello");
                outputStream.flush();

                while(running){
                    if (inputStream != null){
                        AbstractPacket abstractPacket = (AbstractPacket) inputStream.readobject();
                        MessagePacket messagePacket = (MessagePacket) abstractPacket;
                        Message message = messagePacket.getMessage() ;

                        new Handler(Looper.getMainLooper()).post(()-> {
                            TestChatActivity.setChat(message);

                        });
                    }
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printstacktrace();
            Log.d(LOG,"Exception in Appclient  in run()");
           
            if(sslSocket != null){
                try {
                    sslSocket.close();
                } catch (IOException ex) {
                    ex.printstacktrace();
                }
            }
        }
    } 

充当服务器的 ClientHandler 类:

public class ClientHandler extends Thread  {
    private String LOG = "LOG-Test-Aware-Client-handler";
    private ObjectInputStream in;
    private ObjectOutputStream out;

    private boolean running;

    private SSLSocket sslSocket;

    @Getter
    private List<ConnectionListener> connectionListeners;

  
    public ClientHandler(ObjectInputStream in,ObjectOutputStream out,SSLSocket sslSocket,List<ConnectionListener> listener)  {
        this.in = in;
        this.out = out;
        this.sslSocket = sslSocket;
    }


    @Override
    public void run(){
        running = true;
         try {
             while (running) {
                 
                 AbstractPacket abstractPacket = (AbstractPacket) in.readobject();
                 MessagePacket messagePacket = (MessagePacket) abstractPacket;
                 Message message = messagePacket.getMessage() ;

                 Log.d(LOG,message.toString());
                 String plainText = message.getPlaintext(IdentityHandler.getKeyPair().getPrivate());
                 Log.d(LOG,"Plaintext:" +  plainText);
                 onPacket(message);
                 new Handler(Looper.getMainLooper()).post(()-> {
                     TestChatActivity.setChat(message);
                 });
            }

        } catch (IOException | ClassNotFoundException e) {
             e.printstacktrace();
         }
    }

AbstractPacket

package com.example.testaware.models;

import java.io.Serializable;

public abstract class AbstractPacket implements Serializable {
}

MessagePacket

package com.example.testaware.models;

import lombok.Getter;

public class MessagePacket extends AbstractPacket {
    private static final long serialVersionUID = 2545855896325861508L;

    @Getter
    Message message;

    public MessagePacket(Message message) {
        this.message = message;
    }
}

Message

public class Message implements Serializable {
    private byte[] ciphertext;
    private String plaintext;

    @Getter
    private byte[] signature;

    @Getter
    private PublicKey to;
    @Getter
    private PublicKey from;

    public Message(PublicKey to,PublicKey from,String message,PrivateKey privateKey) {
        this.to = to;
        this.from = from;

        Cipher cipher = null;
        try {
            byte[] key = new byte[16];
            (new SecureRandom()).nextBytes(key);

            cipher = Cipher.getInstance("AES/GCM/nopadding");
            cipher.init(Cipher.ENCRYPT_MODE,new SecretKeySpec(key,"AES"));
            this.ciphertext = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));

            // Signature
            ByteArrayOutputStream signatureStream = new ByteArrayOutputStream();
            signatureStream.write(to.getEncoded());
            signatureStream.write(from.getEncoded());
            signatureStream.write(this.ciphertext);

            Signature signature = Signature.getInstance(Constants.SIGNATURE_ALGORITHM);
            signature.initSign(privateKey);
            signature.update(signatureStream.toByteArray());
            this.signature = signature.sign();
            this.plaintext = message;

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | SignatureException | BadPaddingException | IllegalBlockSizeException e) {
            e.printstacktrace();
        }
    }


    public String getPlaintext(PrivateKey privateKey) {
        if(plaintext != null){
            return plaintext;
        }
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(Constants.ENCRYPTION_ALGORITHM_AES);
            byte[] key = new byte[16];
            (new SecureRandom()).nextBytes(key);

            byte[] iv = cipher.getIV();
            GCMParameterSpec gcmspec = cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
            cipher.init(Cipher.DECRYPT_MODE,"AES"),new GCMParameterSpec(128,iv));

            return new String(cipher.doFinal(this.ciphertext),StandardCharsets.UTF_8);
        } catch (BadPaddingException | IllegalBlockSizeException | InvalidKeyException | InvalidParameterSpecException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchPaddingException e) {
            e.printstacktrace();
        }
        return "Decryption Failed.";
    }

谢谢!

解决方法

您的 Message 类不可序列化。