tls 会话通道绑定替代品:java 和 csharp 之间通用材料的搜索

问题描述 投票:0回答:1

由于 java JSSE 尚不支持通道绑定令牌 (CBT),因此我无法使用 RFC 5056 描述的简单“tls-unique”令牌通过委托的 TLS 连接编写 MitM 证明身份验证握手。

如果您想了解为什么 CBT 在应用程序级身份验证中至关重要,请参阅 https://csb.stevekerrison.com/post/2022-01-channel-binding/

“tls-server-end-point”令牌也不可用,但这让我觉得我可以从服务器的公钥证书中编写一些替代品,双方都应该同意(如果没有 MitM)。

所以我想,好吧,让我们对每一侧预期/看到的服务器公钥进行 sha256 并将其用作令牌。

我的问题是,一侧在 java 中,另一侧在 c# 中,并且它们无法生成可比较的通用 byte[] 。我的意思是,API 确实报告了证书、公钥或签名的各种编码格式,但它们在这些语言中的格式并不相同。

我设法让一个 C# 客户端连接到一个 Java 服务器,控制两个源,并在每一端深入研究 RSA 公钥以查找模数和指数(即使如此,BigInteger 的 byte[] 表示仍然稍微有点不准确)关闭一个符号字节),但我不想知道所有这样的证书类型。在未来所有 TLS 变化的喧嚣中,这样做不会长久存在。这意味着维护补丁,尤其是两种语言的补丁。

我的问题是,这些证书、公钥和签名是什么格式,如何解析它们,以及为什么在不同实现中表示它们的 byte[] 不完全相同,因为它们是通过 TCP/TLS 协议发送的?

---- csharp 客户端选项:[主机名 [端口]]--------

using System;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net.Sockets;

class DumpCerts
{
    static void Main(string[] args)
    {
        string server = args.Length>0 ? args[0] : "google.com";
        int port = args.Length>1 ? int.Parse(args[1]) : 443;
        
        using (var client = new TcpClient(server, port)) {
            using (var sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), null))
            {
                sslStream.AuthenticateAsClient(server);
                var remoteCertificate = sslStream.RemoteCertificate as X509Certificate2;

                if (remoteCertificate != null)
                {
                    //Console.WriteLine("Certificate: " + Convert.ToBase64String(remoteCertificate.RawData));
                    //Console.WriteLine("Public Key: " + Convert.ToBase64String(remoteCertificate.GetPublicKey()));
                    
                    X509Chain chain = new X509Chain();
                    chain.Build(remoteCertificate);
                    int i=0;
                    foreach (var element in chain.ChainElements)
                    {
                        var cert = element.Certificate as X509Certificate2;
                        var pubk = cert.PublicKey;
                        //NOTE: cert.GetPublicKey() is a byte[] equals to pubk.EncodedKeyValue.RawData
                        
                        byte[] sig = cert.GetRawCertData();
                        
                        Console.WriteLine("\n\n-----------\npub key["+i+"]: "+cert.Subject
                            +"\npubl key : " + pubk
                            +"\npubl key oid: " + pubk.Oid
                            +"\npubl key oid value: " + pubk.Oid.Value
                            +"\npubk.enc key val  len: " + (pubk.EncodedKeyValue.RawData).Length
                            +"\npubk.enc key val  hex: " + byteArrayToHex(pubk.EncodedKeyValue.RawData)
                            +"\nsig len: " + sig.Length
                            +"\nsig hex: " + byteArrayToHex(sig)
                        );
                        
                        //using (var rsa = RSA.Create()) {
                        //  rsa.ImportSubjectPublicKeyInfo(pubk.EncodedKeyValue.RawData, out _);
                        //  byte[] publicKeyBytes = rsa.ExportSubjectPublicKeyInfo();
                        using (RSA rsa = remoteCertificate.GetRSAPublicKey())
                        {
                            var rsaParameters = rsa.ExportParameters(false);
                            Console.WriteLine("\n"
                            +"\nmodulus  len: "+ (rsaParameters.Modulus.Length)
                            +"\nmodulus  hex: "+ byteArrayToHex(rsaParameters.Modulus)
                            
                            +"\nexponent len: "+ (rsaParameters.Exponent.Length)
                            +"\nexponent hex: "+ byteArrayToHex(rsaParameters.Exponent)
                            );
                        }
                        i++;
                    }
                }
            }
        }
    }

    static bool CertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        return true; // Always accept the certificate
    }
    
    static string byteArrayToHex(byte[] input) {
        int bytesPerLine = 64;
        char[] HEX = "0123456789ABCDEF".ToCharArray();
        StringBuilder result = new StringBuilder();
        byte[] buffer = new byte[bytesPerLine];
        int offset = 0;

        while (offset < input.Length) {
            int length = Math.Min(buffer.Length, input.Length - offset);
            Array.Copy(input, offset, buffer, 0, length);
            
            StringBuilder hex = new StringBuilder();
            StringBuilder ascii = new StringBuilder();
            for (int i = 0; i < length; i++) {
                int v = buffer[i] & 0xFF;
                hex.Append(HEX[v >> 4]);
                hex.Append(HEX[v & 0x0F]);
                ascii.Append((v >= 32 && v <= 127) ? (char)buffer[i] : '.');
            }
            result.AppendFormat("\n\t{0,-" + (2 * bytesPerLine) + "} | {1}", hex.ToString(), ascii.ToString());
            offset += length;
        }
        
        return result.ToString();
    }
    
}

------ java 服务器选项: -i -ks

package clientserverkit;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.net.ssl.ExtendedSSLSession;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

public class DumpCerts {
    private boolean verbose = false;
    
    private String bindIp;
    private int port = 33300;
    
    private String cafilename;
    private char[] capwd;
    
    private String ksfilename;
    private char[] kspwd;
    private char[] keypwd;

    //---
    
    private SSLContext ctx;
    private InetAddress bindAddr;
    
    public static void main(String[] args) throws Exception {
        DumpCerts dc = new DumpCerts();
        dc.parseOptions(args);
        dc.initTls();
        dc.doServer();
    }
    
    void parseOptions(String[] args) throws UnknownHostException {
        for(int i=0; i<args.length; ) {
            String opt = args[i++];
            switch(opt) {
                case "-v": verbose = true; break;
                case "-i": bindIp = args[i++]; break;
                case "-p": port = Integer.parseInt(args[i++]); break;
                case "-ca": cafilename = args[i++]; capwd = args[i++].toCharArray(); break;
                case "-ks": ksfilename = args[i++]; kspwd = args[i++].toCharArray(); keypwd = args[i++].toCharArray(); break;
                default:
                    throw new IllegalArgumentException("Unknown option "+opt);
            }
        }
        
        if(bindIp!=null) {
            bindAddr = bindIp!=null && !bindIp.equalsIgnoreCase("-") ? InetAddress.getByName(bindIp) : null;
        }
    }
    
    private void initTls() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException {
        KeyManager[] kma =null;
        TrustManager[] tma = null;
        
        //--- setup key
        
        Provider bcProvider = SSLContext.getDefault().getProvider();
        
        if(ksfilename!=null) {
            File ksfile = new File(ksfilename);
            if(!ksfile.isFile() && ksfile.canRead())
                throw new IllegalArgumentException("cannot read "+ksfile);
            
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm(), bcProvider);
            KeyStore ks = KeyStore.getInstance(ksfile, kspwd);
            kmf.init(ks, kspwd);
            Arrays.fill(kspwd, 's');
            Arrays.fill(keypwd, 'k');
            kma = kmf.getKeyManagers();
        }
        
        
        //--- setup trusted CA
        
        if(cafilename!=null) {
            File cafile = new File(cafilename);
            if(!cafile.isFile() && cafile.canRead()){
                throw new IllegalArgumentException("cannot read "+cafile);
            }
            
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(), bcProvider);
            KeyStore caks = KeyStore.getInstance(cafile, capwd);
            Arrays.fill(capwd, 'c');
            tmf.init(caks);
            tma = tmf.getTrustManagers();
        }   
        
        //--- make tls ctx and pull socket factories
        
        SecureRandom sr = new SecureRandom();
        ctx = SSLContext.getInstance("TLS", bcProvider);
        //ctx = SSLContext.getInstance("TLSv1.2");
        ctx.init(kma, tma, sr);
    }
    
    void doServer() throws IOException {
        ServerSocket serverSocket = null;
        try {
            SSLServerSocketFactory sssf = ctx.getServerSocketFactory();
            SSLServerSocket sss = (SSLServerSocket)sssf.createServerSocket(port, 50, bindAddr);
            //sss.setNeedClientAuth(true);//mandatory
            sss.setWantClientAuth(true);//optional
            serverSocket = sss;
            while(!Thread.interrupted()) {
                try(Socket s = serverSocket.accept()) {
                    dumpInfo(s, verbose);
                }
            }
        } finally {
            if(serverSocket!=null)
                serverSocket.close();
        }
    }
    
    static void dumpInfo(Socket s, boolean verbose) {
        System.out.println("\n\n===== socket dump ===================================================================================");
        System.out.println("local skt : "+s.getLocalSocketAddress());
        System.out.println("remote skt : "+s.getRemoteSocketAddress());
        if(s instanceof SSLSocket ss) {
            SSLSession sess = ss.getSession();
            System.out.println("sess proto    : "+sess.getProtocol());
            System.out.println("sess cipher suite: "+sess.getCipherSuite());
            System.out.println("sess id: (hex) "+bytesToHex(sess.getId()));
            System.out.println("sess peer host: "+sess.getPeerHost());
            System.out.println("sess peer port: "+sess.getPeerPort());
            
            System.out.println("sess local principal  : "+sess.getLocalPrincipal());
            dumpCerts("local", sess.getLocalCertificates(), verbose);
            
            try {
                System.out.println("sess peer principal: "+sess.getPeerPrincipal());
                dumpCerts("peer", sess.getPeerCertificates(), verbose);
            } catch (SSLPeerUnverifiedException e) {
                System.out.println("(peer unverified)");
            }
            
            for(String n: sess.getValueNames()) {
                System.out.println("sess value["=="]: "+sess.getValue(n));
            }
            System.out.println("sess creation time: "+new Date(sess.getCreationTime()));
            System.out.println("sess access time  : "+new Date(sess.getLastAccessedTime()));
            System.out.println("sess pkt buff : "+sess.getPacketBufferSize());
            System.out.println("sess app buff : "+sess.getApplicationBufferSize());
            
            SSLSessionContext ctx = sess.getSessionContext();
            if(ctx!=null) {
                System.out.println("sess ctx sess cache sz: "+ctx.getSessionCacheSize());
                System.out.println("sess ctx sess timeout : "+ctx.getSessionTimeout());
            }
            
            if(sess instanceof ExtendedSSLSession xsession) {
                List<SNIServerName> list = xsession.getRequestedServerNames();
                if(list!=null)
                    list.forEach(sni -> System.out.println("xsess sni: "+sni.toString()));
                System.out.println("xsess local supported algos: "+Arrays.asList(xsession.getLocalSupportedSignatureAlgorithms()));
                System.out.println("xsess peer  supported algos: "+Arrays.asList(xsession.getPeerSupportedSignatureAlgorithms()));
            }
        }
    }

    
    static void dumpCerts(String prefix, Certificate[] certs, boolean verbose) {
        if(certs==null) {
            System.out.println("(no "+prefix+" certs)");
            return;
        }
        
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            for(int i=0; i<certs.length; i++) {
                md.reset();
                PublicKey publicKey = certs[i].getPublicKey();
                byte[] pubk = publicKey.getEncoded();
                byte[] sha256 = md.digest(pubk);
                System.out.println("\n-----certs["+i+"]------------");
                System.out.println(prefix+" certs["+i+"] publ key algo="+publicKey.getAlgorithm()+", format="+publicKey.getFormat());
                System.out.println(prefix+" certs["+i+"] publ key enc len : "+pubk.length);
                System.out.println(prefix+" certs["+i+"] publ key enc hex : "+bytesToHex(pubk));
                System.out.println(prefix+" certs["+i+"] sha256(publ key enc) : "+bytesToHex(sha256));
                
                if(certs[i] instanceof X509Certificate xcert) {
                    try {
                        byte[] tbsder = xcert.getTBSCertificate();
                        System.out.println(prefix+" certs["+i+"] tbsder len : "+tbsder.length);
                        System.out.println(prefix+" certs["+i+"] tbsder hex : "+bytesToHex(tbsder));
                    } catch (CertificateEncodingException e) {
                        e.printStackTrace();
                    }
                    byte[] sig = xcert.getSignature();
                    System.out.println(prefix+" certs["+i+"] sig len : "+sig.length);
                    System.out.println(prefix+" certs["+i+"] sig hex : "+bytesToHex(sig));
                }
                
                if(publicKey instanceof RSAPublicKey rsapk) {
                    System.out.println(prefix+" certs["+i+"] len(modulus) : "+rsapk.getModulus().toByteArray().length);
                    System.out.println(prefix+" certs["+i+"] hex(modulus) : "+bytesToHex(rsapk.getModulus().toByteArray()));
                    
                    System.out.println(prefix+" certs["+i+"] len(exponent): "+rsapk.getPublicExponent().toByteArray().length);
                    System.out.println(prefix+" certs["+i+"] hex(exponent): "+bytesToHex(rsapk.getPublicExponent().toByteArray()));
                }
            }
            if(verbose) {
                for(int i=0; i<certs.length; i++) {
                    System.out.println("\n-----"+prefix+" certs["+i+"].toString()---------\n"+certs[i]);
                }
            }
        } catch (NoSuchAlgorithmException e) {
            System.out.println("(hash algo not found): "+e);
        }
        
    }
    
    public static String bytesToHex(byte[] input) {
        int bytesPerLine = 64;
        char[] HEX = "0123456789ABCDEF".toCharArray();
        
        StringBuilder result = new StringBuilder();
        byte[] buffer = new byte[bytesPerLine];
        int offset = 0;
        
        while (offset < input.length) {
            int length = Math.min(buffer.length, input.length - offset);
            System.arraycopy(input, offset, buffer, 0, length);
            
            StringBuilder hex = new StringBuilder();
            StringBuilder ascii = new StringBuilder();
            for (int i = 0; i < length; i++) {
                int v = buffer[i] & 0xFF;
                hex.append(HEX[v >>> 4]);
                hex.append(HEX[v & 0x0F]);
                ascii.append((v >= 32 && v <= 127) ? (char)buffer[i] : '.');
            }
            
            //result.append("\n\t").append(hex).append(" | ").append(ascii);
            result.append(String.format("\n\t%-"+(2*bytesPerLine)+"s | %s", hex.toString(), ascii.toString()));
            offset += length;
        }
        return result.toString();
    }
    
}
java c# ssl cbt
1个回答
0
投票

找到了。 Csharp 需要拉

byte[] certder = cert.GetRawCertData();

而java需要拉

byte[] certder = certs[i].getEncoded();
© www.soinside.com 2019 - 2024. All rights reserved.