国密https访问

news2024/11/17 5:55:58

前言

现在的SSL的加密算法实际上主要是国际算法,包括JDK,Go等语言也仅支持国际算法加密(毕竟是国外开源项目),hash。随着国密算法的普及,比如openssl就支持国密了,还要新版本的Linux内核也开始支持,以openssl为例:

那么如果需要国密证书,或者访问国密https的时候就需要特定的sslsocket的握手算法

传输层密码协议(TLCP)

TLCP是中国基于TLS 1.1和1.2协议定制而成的协议,对应的中国国家标准为:

  • GB/T 38636-2020:传输层密码协议

该协议与TLS协议的最重大区别,就是要求通信端提供两个证书:认证证书和加密证书。其中认证证书与TLS协议使用的证书功能类似,用于对通信端的身份进行验证。而加密证书则为TLCP协议独有,它只会用于密钥交换。

开源套件

常用的国密开源套件有BouncyCastle、Kona,BouncyCastle时间比较久远了,用的比较多,比如OOM的坑,Kona是腾讯开源的国密套件,开始基于BouncyCastle,后面重写了,分别使用者两个套件试试沃通搭建的SM2SSL证书测试网站欢迎访问沃通基于国密算法的https加密解决方案演示网站icon-default.png?t=N7T8https://sm2test.ovssl.cn/

和 中国银行官网icon-default.png?t=N7T8https://ebssec.boc.cn/

其中沃通是支持国密和国际算法的无缝切换的,因为有双证书

 

BouncyCastle

https://www.bouncycastle.org/java.html,github使用MIT license

 

截止当前的日期

 

不支持国密源码分析

bouncycastle的ssl支持org.bouncycastle.jsse.provider.BouncyCastleJsseProvider

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk18on</artifactId>
            <version>1.76</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bctls-jdk18on</artifactId>
            <version>1.76</version>
        </dependency>

定义了protocol

 

都是标准的,而这些支持的加密套件呢,实际上国密SSL(中国银行官网)使用的是TLSV1.1变化版本,这里实际执行的类是org.bouncycastle.jsse.provider.ProvSSLContextSpi的

createSupportedCipherSuiteMap

定义了使用的加密套件,然而国密的套件实际上有定义

org.bouncycastle.tls.CipherSuite

RFC 8998

RFC 8998规范将国密算法要素应用到了TLS 1.3协议中。KonaSSL实现了该规范定义的椭圆曲线curveSM2(41),签名机制sm2sig_sm3(0x0708)和密码套件TLS_SM4_GCB_SM3(0x00C6)。

所以,虽然支持RFC 8998标准,但是tls不支持国密通信,因为没有加入加密套件,也没有定义协议版本

笔者自己加了一个,不过发现修改的地方不少,国密ssl版本号定义0x0101,跟主流的ssl差别很大 

Kona

企鹅开源的,在1.0.5版本不再基于BouncyCastle

腾讯Kona国密套件使用的许可协议是GNU GPL v2.0 license with Classpath Exception,这也正是OpenJDK使用的许可协议。

腾讯Kona国密套件包含四个Java Security Provider

  • KonaCrypto,它是一个Java Cryptography Extension(JCE)实现,遵循标准的Java Cryptography Architecture(JCA)框架实现了国密基础算法SM2,SM3和SM4。
  • KonaPKIX,它实现了国密证书的解析及其证书链验证,并可加载和创建包含国密证书的密钥库(Key Store)文件。
  • KonaSSL,它实现了中国的传输层密码协议(TLCP),并遵循RFC 8998规范将国密基础算法应用到了TLS 1.3协议中。
  • Kona,它没有直接实现任何功能,而是将KonaCrypto,KonaPKIX和KonaSSL中的特性进行了简单的封装,以方便用户仅使用这一个Provider就能够调用上述三个Provider中的全部功能。一般地,建议使用这个Provider。

--来源于腾讯Kona国密套件:从基础算法到安全协议-腾讯云开发者社区-腾讯云

socket支持

以kona为例,实际上BouncyCastle也有TLS模块,但是貌似没做国密的协议簇,只能使用国际标准,这里使用不知道初始原作者是谁的gmssl的demo代码,引入pom

        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-provider</artifactId>
            <version>${kona.version}</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-crypto</artifactId>
            <version>${kona.version}</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-ssl</artifactId>
            <version>${kona.version}</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.kona</groupId>
            <artifactId>kona-pkix</artifactId>
            <version>${kona.version}</version>
        </dependency>

然后通过socket方式支持国密https,以中国银行官网为测试

package org.example;

import com.tencent.kona.KonaProvider;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;

import java.net.*;
import java.io.*;
import java.security.*;

import javax.net.*;
import javax.net.ssl.*;

public class SocketGet {
    public static void main(String[] args) {
        SocketFactory fact = null;
        SSLSocket socket = null;

//        String addr = "sm2test.ovssl.cn";
        String addr = "ebssec.boc.cn";
        int port = 443;
        String uri = "/";

        try {
            if (args.length > 0) {
                addr = args[0];
                port = Integer.parseInt(args[1]);
                uri = args[2];
            }

            System.out.println("\r\naddr=" + addr);
            System.out.println("port=" + port);
            System.out.println("uri=" + uri);

            // 加载国密提供者
//            Security.insertProviderAt(new BouncyCastleProvider(), 1);
//            Security.insertProviderAt(new BouncyCastleJsseProvider(), 2);
            Security.insertProviderAt(new KonaProvider(), 1);

            fact = createSocketFactory(null, null);
            socket = (SSLSocket) fact.createSocket();
            socket.setTcpNoDelay(true);

            System.out.println("\r\nGM SSL connecting...");
            socket.connect(new InetSocketAddress(addr, port), 5000);
            socket.setTcpNoDelay(true);
            socket.startHandshake();

            System.out.println("Connected!\n");

            DataInputStream in = new DataInputStream(socket.getInputStream());
            OutputStream out = socket.getOutputStream();

            String s = "GET " + uri + " HTTP/1.1\r\n";
            s += "Accept: */*\r\n";
            s += "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)\r\n";
            s += "Host: " + addr + (port == 443 ? "" : ":" + port) + "\r\n";
            s += "Connection: Close\r\n";
            s += "\r\n";
            out.write(s.getBytes());
            out.flush();

            // 读取HTTP头
            while (true) {
                byte[] lineBuffer = ReadLine.read(in);
                if (lineBuffer == null || lineBuffer.length == 0) {
                    System.out.println();
                    break;
                }
                String line = new String(lineBuffer);
                System.out.println(line);
            }

            // 读取HTTP内容
            {
                byte[] buf = new byte[1024];
                while (true) {
                    int len = in.read(buf);
                    if (len == -1) {
                        break;
                    }
                    System.out.println(new String(buf, 0, len));
                }
            }

            in.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (Exception e) {
            }
        }
    }

    private static SSLSocketFactory createSocketFactory(KeyStore kepair, char[] pwd) throws Exception {
        X509TrustManager[] trust = {new MyTrustAllManager()};

        KeyManager[] kms = null;
        if (kepair != null) {
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(kepair, pwd);
            kms = kmf.getKeyManagers();
        }

        // 使用国密SSL
//        String protocol = "TLSV1.1";
        String protocol = "TLCPv1.1";
        String provider = KonaProvider.NAME;
//        String provider = BouncyCastleJsseProvider.PROVIDER_NAME;
        SSLContext ctx = SSLContext.getInstance(protocol, provider);

        java.security.SecureRandom secureRandom = new java.security.SecureRandom();
        ctx.init(kms, trust, secureRandom);

        SSLSocketFactory factory = ctx.getSocketFactory();
        return factory;
    }


}

package org.example;

import java.io.*;
import java.net.SocketException;

class ReadLine {
    public static final byte[] CRLF = {'\r', '\n'};
    public static final byte CR = '\r';
    public static final byte LF = '\n';

    private static final int LINE_MAX_SIZE = 16384;

    public static byte[] read(DataInputStream in) throws IOException, SocketException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream s = new DataOutputStream(baos);
        boolean previousIsCR = false;

        int len = 0;
        byte b = 0;

        try {
            b = in.readByte();
            len++;
        } catch (EOFException e) {
            return new byte[0];
        }

        while (true) {
            if (b == LF) {
                if (previousIsCR) {
                    s.flush();
                    byte[] rs = baos.toByteArray();
                    s.close();
                    return rs;
                } else {
                    s.flush();
                    byte[] rs = baos.toByteArray();
                    s.close();
                    return rs;
                }
            } else if (b == CR) {
                if (previousIsCR) {
                    s.writeByte(CR);
                }
                previousIsCR = true;
            } else {
                if (previousIsCR) {
                    s.writeByte(CR);
                }
                previousIsCR = false;
                s.write(b);
            }

            if (len > LINE_MAX_SIZE) {
                s.close();
                throw new IOException("Reach line size limit");
            }

            try {
                b = in.readByte();
                len++;
            } catch (EOFException e) {
                s.flush();
                byte[] rs = baos.toByteArray();
                s.close();
                return rs;
            }
        }
    }
}


package org.example;

import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;

class MyTrustAllManager implements X509TrustManager
{
    private X509Certificate[] issuers;

    public MyTrustAllManager()
    {
        this.issuers = new X509Certificate[0];
    }

    public X509Certificate[] getAcceptedIssuers()
    {
        return issuers ;
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType)
    {}

    public void checkServerTrusted(X509Certificate[] chain, String authType)
    {}
}

核心步骤

实际上就是2步:

1. 注册国密

Security.insertProviderAt(new KonaProvider(), 1);

2. 使用国密sslcontext

SSLContext ctx = SSLContext.getInstance(protocol, provider);

实际上这步是核心,BouncyCastle不支持国密就是因为国密protocol在BouncyCastle没有定义

访问后如下:

addr=ebssec.boc.cn
port=443
uri=/

GM SSL connecting...
Connected!

HTTP/1.1 200 OK
Date: Sat, 21 Oct 2023 11:55:15 GMT
Last-Modified: Sat, 27 Jun 2015 16:48:38 GMT
Accept-Ranges: bytes
Content-Length: 156
Cache-Control: max-age=300
Expires: Sat, 21 Oct 2023 12:00:15 GMT
Vary: Accept-Encoding,User-Agent
Content-Type: text/html
Connection:close

<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=/boc15/login.html"><meta name="renderer" content="ie-stand"></head><body></body></html>

简单源码分析

协议支持,通过注册provider

 

然后com.tencent.kona.sun.security.ssl.CipherSuite定义了TLCP的加密套件,所以可以匹配国密,当然BouncyCastle的tls也可以在上面支持,不过这个就跟kona区别不大,毕竟开始kona也是基于BouncyCastle开发的

有了加密套件,那么按照jce和jsse的模式进行ssl握手,然后加密传输数据。

httpclient

实际上就是那2行代码,再核心一点就是国密算法和加密套件的注册和使用 

public class HttpClientDemo {
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
        Security.insertProviderAt(new KonaProvider(), 1);

        SSLContext sslContext = SSLContext.getInstance("TLCPv1.1", KonaProvider.NAME);
        sslContext.init(null, new TrustManager[] {new MyTrustAllManager()}, new SecureRandom());
        CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
        HttpGet get = new HttpGet("https://ebssec.boc.cn");
        CloseableHttpResponse response = client.execute(get);
        System.out.println(response.getStatusLine().toString());
        Header[] headers = response.getAllHeaders();
        for(Header header : headers ){
            System.out.println(header.getName() +" : "+header.getName());
        }
        HttpEntity e = response.getEntity();
        System.out.println(EntityUtils.toString(e,"UTF-8"));
    }
}

运行后,结果如下

HTTP/1.1 200 OK
Date : Date
Last-Modified : Last-Modified
Accept-Ranges : Accept-Ranges
Cache-Control : Cache-Control
Expires : Expires
Vary : Vary
Keep-Alive : Keep-Alive
Connection : Connection
Content-Type : Content-Type
<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=/boc15/login.html"><meta name="renderer" content="ie-stand"></head><body></body></html>

 相当于把刚刚,那一系列的代码用Apache的jar实现了,开源方便👍🏻

总结

国密SSL实际上就是TLS1.1和TLS1.2的结合体,一般而言使用TLS1.1即可,协议版本定义0x0101,而且ssl握手需要双证书认证:握手中发送签名证书、加密证书,一般而言签名证书先发,后发加密证书。签名证书仅用于验证身份,私钥自己生成,由CA机构签发;加密证书用于数据加密,CA机构生成并持有私钥。国密SSL实际上跟普通SSL区别不大,核心是使用国密算法密码套件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

识别准确率竟如此高,实时语音识别服务

前言 本文将介绍一个准确率非常高的语音识别框架&#xff0c;那就是FunASR&#xff0c;这个框架的模型训练数据超过几万个小时&#xff0c;经过测试&#xff0c;准确率非常高。本文将介绍如何启动WebSocket服务和Android调用这个服务来实时识别&#xff0c;一边说话一边出结果…

2.卷积神经网络(CNN)

一句话引入&#xff1a; 如果我们要做图像识别&#xff0c;用的是一个200x200的图片&#xff0c;那么BP神经网络的输入层就需要40000个神经元&#xff0c;因为是全连接&#xff0c;所以整个BP神经网络的参数量就是160亿个&#xff0c;显然不能这样来训练网络&#xff0c;所以我…

【RNA structures】RNA转录的重构和前沿测序技术

文章目录 RNA转录重建1 先简单介绍一下测序相关技术2 Map to Genome Methods2.1 Step1 Mapping reads to the genome2.2 Step2 Deal with spliced reads2.3 Step 3 Resolve individual transcripts and their expression levels 3 Align-de-novo approaches3.1 Step 1: Generat…

MySQL——八、MySQL索引视图

MySQL 一、视图1、什么是视图2、为什么需要视图3、视图的作用和优点4、创建视图5、视图使用规则6、修改视图7、删除视图 二、索引1、什么是索引2、索引优缺点3、索引分类4、索引的设计原则5、创建索引5.1 创建表是创建索引5.2 create index5.3 ALTER TABLE 6、删除索引7、MySQL…

英语——歌诀篇——歌诀记忆法

介词用法速记歌 年月季前要用in&#xff0c; 日子前面却不行。 遇到几号要用on&#xff0c; 上午下午又用in。 要说某时上下午&#xff0c; 用on换in才可行。 午夜黄昏和黎明&#xff0c; 要用at不用in。 差儿分到几点&#xff0c; 写个“to”在中间。 若是几点过几分&#xf…

操作系统【OS】进程的通信

共享存储 各个进程对共享空间的访问应该是互斥的&#xff08;可以使用P、V操作&#xff09;低级通信 基于数据结构的共享速度慢、限制多高级通信 基于存储区的共享数据的形式、存放的位置由通信进程控制速度快 消息传递 进程间的数据交换以格式化的消息为单位 消息头&#…

《windows核心编程》第2章 UNICODE字符

一、基础内容 1.1 UNICODE和UTF-8 下面是我个人的理解 UNICODE用2个字节来代表一个字符 UTF-8用1-4个字节来表示一个字符。可变长度的实现原理是ASCII字符只有7位&#xff0c;首位如果是1表示不是ASCII&#xff0c;说明这个字符和后面的字符联合起来形成新字符。 1.2 字符的…

【经历】跨境电商公司目前已在职近2年->丰富且珍贵

我入职了跨境电商公司 *背景 上篇说我在2021-11月离职了&#xff0c;交接期间已经拿到了新公司的offer&#xff0c;然后因上家公司项目交接时间比较长(原因在上篇)&#xff0c;导致新公司这边延迟了两次入职的时间&#xff0c;最后结果是直接无缝衔接了新公司&#xff08;周五…

#电子电器架构 —— 车载网关初入门

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 PS:小细节,本文字数7000+,详细描述了网关在车载框架中的具体性能设置。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他…

IOS(刘海/留海/流海)屏幕判断

IPhone 8 没刘海屏幕 示例代码: // // ViewController.m // IOS_SAFEAREA_TEST // // Created by Hacker X on 2023/10/14. //#import "ViewController.h"interface ViewController ()endimplementation ViewController- (void)viewDidLoad {[super viewDidLoad]…

类的属性和方法(java)

类和对象的使用 创建类&#xff0c;设计类的成员创建类的对象通过“对象.属性”或“对象.方法”调用对象的结构 代码 public class Per {public static void main(String[] args) {// TODO Auto-generated method stub//创建Person类的对象Person p1 new Person();//Scanne…

清除浮动会对性能有什么影响吗?

清除浮动本身不会对性能产生显著的影响。它只是一种修复布局问题的技术&#xff0c;主要影响的是布局的正确性和可靠性&#xff0c;而不是性能。 然而&#xff0c;使用不当的清除浮动方法可能会对性能产生一些间接的影响。以下是一些与清除浮动相关的潜在性能问题&#xff1a;…

城市正视图(Urban Elevations, ACM/ICPC World Finals 1992, UVa221)rust解法

如图5-4所示&#xff0c;有n&#xff08;n≤100&#xff09;个建筑物。左侧是俯视图&#xff08;左上角为建筑物编号&#xff0c;右下角为高度&#xff09;&#xff0c;右侧是从南向北看的正视图。 输入每个建筑物左下角坐标&#xff08;即x、y坐标的最小值&#xff09;、宽度…

华山论剑:2nm芯片工艺谁更强?

在当今高速发展的科技时代&#xff0c;芯片工艺的重要性不言而喻。芯片制造技术不断突破&#xff0c;使得电子产品性能更高、功能更强大&#xff0c;同时也推动了整个科技行业的快速发展。本文探讨下三星、台积电和英特尔三大芯片制造巨头的工艺技术。 英特尔未来几年的主要目标…

Github Actions实现Spring Boot自动化部署(第二弹)

Github Actions实现Spring Boot自动化部署&#xff08;第二弹&#xff09; 前言 ​ 今天就来讲述一下如何使用GitHub结合Actions实现Spring Boot程序从提交代码到打包、容器化、部署全过程自动化。首先咱们得现有一个能够在本地运行的Spring Boot程序&#xff0c;并且在Githu…

3D测量之圆孔测量 拟合圆 点云变换

0. 效果展示 1. 圆孔测量介绍 此文中的圆孔测量是一项3D视觉技术,旨在精确测量物体表面上的圆孔的直径和中心坐标。通过使用高精度3D相机(线激光轮廓仪或结构体等)采集原始点云数据,通过3D视觉算法能够快速、准确地分析物体上的圆孔特征,为制造和工程领域提供了强大的测量…

FFT64点傅里叶变换verilog蝶形运算,代码和视频

名称&#xff1a;FFT64点verilog傅里叶变换 软件&#xff1a;Quartus 语言&#xff1a;Verilog 代码功能&#xff1a; 使用verilog代码实现64点FFT变换&#xff0c;使用蝶形运算实现傅里叶变换 演示视频&#xff1a;http://www.hdlcode.com/index.php?mhome&cView&…

SpringCloud学习笔记(上):服务注册与发现:Eureka、Zookeeper、Consul+负载均衡服务调用:Ribbon

壹、零基础 一、微服务架构零基础理论入门 SpringCloud分布式微服务架构的一站式解决方案&#xff0c;是多种微服务架构落地技术的集合体&#xff0c;俗称微服务全家桶。 二、从2.2.x和H版开始说起 springboot版本选择&#xff1a; git源码地址&#xff1a;https://github.…

渗透测试工具(3)Burpsuite

笔记目录 渗透测试工具(1)wireshark渗透测试工具(2)Nmap渗透测试工具(3)Burpsuite 1.简介 是Web应用程序测试&#xff0c;请求的拦截和修改,扫描web应用程序漏洞,以暴力破解登陆表单,执行会话令牌等多种的随机性检查。 (1)模块介绍 ①Intercept&#xff1a;用于显示和修改Ht…

Java面试题-UDP\TCP\HTTP

UDP UDP特性 &#xff08;1&#xff09;UDP是无连接的&#xff1a;发送数据之前不需要像TCP一样建立连接&#xff0c;也不需要释放连接&#xff0c;所以减少了发送和接收数据的开销 &#xff08;2&#xff09;UDP 使用尽最大努力交付&#xff1a;即不保证可靠交付 &#xff0…