【问题标题】:tls 1.2 strange cipher suitestls 1.2 奇怪的密码套件
【发布时间】:2015-12-20 21:42:59
【问题描述】:

我正在开发一个将实现 TLS 协议的 java 应用程序。因为我最近开始实施它。在开发握手类型的记录协议实现时,我从 google chrome 和 mozilla firefox 得到了奇怪的密码套件。正如this 文档所说,在握手协议中有类型、握手长度、版本、随机、会话 ID 长度、会话 ID、密码套件长度、密码套件......

我得到了正确的所有字段(在密码套件之前),但在密码套件上,我从 google chrome 和 firefox 获得了奇怪的值。

这是我接受 4040 端口连接的代码。

   package server;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.*;
import java.security.KeyStore;
import java.security.SecureRandom;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class Server extends ServerSocket {
    public Server() throws IOException {
        super();
        // TODO Auto-generated constructor stub
    }
    public static void main(String args[]) 
    {
        System.setProperty("java.net.ssl.trustStore", "mam.store");
        System.setProperty("java.net.ssl.keyStorePassword", "mamoon");
        try {
            /*KeyStore ks  = KeyStore.getInstance("JKS");
            InputStream in = new FileInputStream("/Users/Ahmed/Desktop/mam.store");
            ks.load(in, "mamoon".toCharArray());
            in.close();
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks,"mamoon".toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ks);
            SSLContext con = SSLContext.getInstance("TLSv1.2");
            con.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            SSLServerSocket socket = (SSLServerSocket)con.getServerSocketFactory().createServerSocket(4040);
            */
            ServerSocket socket = new ServerSocket(4040);
            System.out.println("Server is up.");

            Socket s = socket.accept();
            PrintWriter w = new PrintWriter(s.getOutputStream());
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(),"ISO-8859-1"));
            String line;
            String packet = "";
            int l=0;
            while((line = br.readLine()) != null)
            {
                packet += line;
                System.out.println(line.length());
                if(packet.length() >= 100)
                {
                for(char c:packet.toCharArray())
                    System.out.print((int)c + "   ");
                System.out.println();
                new TLS(packet);
                break;
                }
            }
            System.out.println(l);
            //w.write("<html><body>hello world</body> </html>");
            //s.close();
            socket.close();

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这是解码 TLS 数据包的类。

package server;

import java.math.BigInteger;
import java.nio.ByteBuffer;

public class TLS {
    String start = (char)22 + "" + (char)3;
    float version;
    int packetLength,handshakeLength,sessionIdLength;
    String random = "",sessionId = "";
    String message;
    String packetType,encryption;
    int cipherSuitsLength;
    int cipherSuitesId[];

    TLS(String packet) throws Exception
    {
        if(packet.startsWith(start))
        {
            version = Float.parseFloat((int)packet.charAt(1) + "."+(int)packet.charAt(2));
            packetLength = new BigInteger(new byte[]{(byte)packet.charAt(3),(byte)packet.charAt(4)}).intValue();
            System.out.println(packetLength);
                    //(byte)packet.charAt(3) << 8 | ((byte)packet.charAt(4) & 0xFF);
            if((int)packet.charAt(5) == 1)
                packetType = "Client Hello";
            else if((int)packet.charAt(5) == 2)
                packetType = "Server Hello";
            else if((int)packet.charAt(5) == 11)
                packetType = "Certificate";
            else if((int)packet.charAt(5) == 12)
                packetType = "Server Key Exchange";
            else if((int)packet.charAt(5) == 13)
                packetType = "Certificate Request";
            else if((int)packet.charAt(5) == 14)
                packetType = "Server Done";
            else if((int)packet.charAt(5) == 15)
                packetType = "Certificate Verify";
            else if((int)packet.charAt(5) == 16)
                packetType = "Client Key Exchange";
            else if((int)packet.charAt(5) == 20)
                packetType = "Finished";
            handshakeLength = new BigInteger(new byte[] {(byte)packet.charAt(6) , (byte)packet.charAt(7), (byte)packet.charAt(8)}).intValue(); 
                    //(byte)packet.charAt(7) << 16 | ((byte)packet.charAt(8) << 8 & 0xFF) | ((byte));
            System.out.println(handshakeLength);
            version = Float.parseFloat((int)packet.charAt(9) + "."+(int)packet.charAt(10));
            System.out.println(version);
            for(int a=11;a<11+32;a++)
            {   random += packet.charAt(a);
            System.out.println((int)packet.charAt(a));
            }
            System.out.println(random.length());
            sessionIdLength = (int)packet.charAt(43);
            System.out.println(sessionIdLength);
            int c = 44+sessionIdLength;
            for(int a=44;a<44+sessionIdLength;a++)
            {
                sessionId += packet.charAt(a);
            }
            System.out.println(sessionId);
            cipherSuitsLength = new BigInteger(new byte[] {(byte)packet.charAt(c),(byte) packet.charAt(c+1)}).intValue();
            System.out.println(cipherSuitsLength);
            cipherSuitesId = new int[cipherSuitsLength/2];
            c+=2;
            for(int a=0;a<cipherSuitesId.length;a++)
            {
                cipherSuitesId[a] = new BigInteger(new byte[]{(byte)packet.charAt(c),(byte)packet.charAt(c+1)}).intValue();
                c+=2;
                System.out.println(cipherSuitesId[a]);
            }
        }
        else
            throw new Exception("Not a TLS packet.");
    }
    public void processTLS(String packet)
    {

    }
}

我的代码输出是

Server is up.
51
19
14
61
22   3   1   0   168   1   0   0   164   3   3   57   220   109   126   106   16   106   75   232   65   206   177   89   90   5   241   76   167   135   150   97   51   67   213   33   60   209   73   137   119   119   80   0   0   30   192   43   192   47   192   192   9   192   19   192   20   192   7   192   17   0   51   0   57   0   47   0   53   0   0   5   0   4   1   0   0   93   255   1   0   1   0   0   0   8   0   6   0   23   0   24   0   25   0   11   0   2   1   0   0   35   0   0   51   116   0   0   0   16   0   23   0   21   2   104   50   8   115   112   100   121   47   51   46   49   8   104   116   116   112   47   49   46   49   0   5   0   5   1   0   0   0   0   0   
168
164
3.3
57
220
109
126
106
16
106
75
232
65
206
177
89
90
5
241
76
167
135
150
97
51
67
213
33
60
209
73
137
119
119
80
32
0

30
-16341
-16337
-16192
2496
5056
5312
1984
4352
13056
14592
12032
13568
5
4
256
0

如您所见,密码套件长度为 0 30,这意味着收到了 15 个密码套件。但我确信没有像 192 43 这样的密码套件 ID 等等。请帮我解决这个问题。

【问题讨论】:

  • 这是出于学术目的还是您真的打算使用该实现?
  • @Robert 我将使用该实现。我知道这个实现现在很可悲,但我会为每个 TLS 协议做得更好和分开。但我坚持使用密码套件。
  • 这是实施的开始,我正在同时进行测试阶段。

标签: java tls1.2


【解决方案1】:

您正在解析每个数据包,就好像它包含密码套件一样。他们没有。

你也完全走错了路。 TLS 记录是二进制的,String 不是二进制数据的容器。

而且,使用这种结构的代码,你真的永远不会有任何收获。在你领先的时候退出,或者看看真正的实现是如何编码的。什么都没有。

【讨论】:

  • 这是只针对客户端 hello 数据包的实现。我把每个字节都当作一个无符号整数。我知道现在的实施真的很可悲。我会做得更好。
  • 无论如何,谢谢您的回答,我想出了问题所在。 192 43 是 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 的 ID。
  • 不管你对字符串中的字符做什么,它仍然不是二进制数据的容器,它的字符不一定将 1::1 映射到原始字节. byte[]->String->byte[]之间的往返不能保证有效,也完全没有意义。只需使用byte[]
  • 是的。使用字节数组是正确的方法。我还有一个问题。识别 tls 数据包的正确方法是什么?
【解决方案2】:

192 43 是 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 的 ID。

Wireshark packet capture. TLS 1.2 client hello packet.

Here 是密码套件 ID 的完整列表。

【讨论】:

【解决方案3】:

是的,还有另一个问题,第三个密码套件 ID 是 192 192,但不存在这样的密码套件。那是因为 br.readLine() 声明。它将跳过字符 10 并将其视为换行符,但实际上是信息。使用字节数组读取数据是正确的方式。

DataInputStream s = new DataInputStream(socket.getInputStream());
byte data[] = new byte[s.available()];
s.readFully(data);

这个实现会给出实际的数据。

【讨论】:

  • 但是使用available() 是错误的方式。请参阅 Javadoc。
  • available() 方法给出了可用的字节数(计数)。阅读 javadoc 后,我没有看到此功能有任何问题。如果您知道或意识到任何问题,请告诉我。您的帮助将不胜感激。
猜你喜欢
  • 2018-10-12
  • 2018-11-21
  • 1970-01-01
  • 2018-12-11
  • 2020-06-30
  • 2018-02-07
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
相关资源
最近更新 更多