国密SSL协议之Java编程

简介: Java自身通过JCE和JSSE支持标准的SSL协议,但并不支持国密SSL协议。本文描述了Java使用国密JCE和国密JSSE开发一个简单的客户端程序,连接国密Web网站,发送HTTP请求,并接收HTTP应答。

背景

  Java自身通过JCE和JSSE支持标准的SSL协议,但并不支持国密SSL协议。本文描述了Java使用国密JCE和国密JSSE开发一个简单的客户端程序,连接国密Web网站,发送HTTP请求,并接收HTTP应答。

环境

JRE是jre8。
国密JCE和国密JSSE。下载参 
https://www.gmssl.cn/gmssl/index.jsp?go=gmsdk
gmjce.jar和gmjsse.jar放到jre的lib/ext/目录下

源码

package cn.gmssl.test;

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

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

public class SocketGet
{
    public static void main(String[] args)
    {
        SocketFactory fact = null;
        SSLSocket socket = null;
        
        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 cn.gmssl.jce.provider.GMJCE(), 1);
            Security.insertProviderAt(new cn.gmssl.jsse.provider.GMJSSE(), 2);

            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 = cn.gmssl.jsse.provider.GMJSSE.GMSSLv11;
           String provider = cn.gmssl.jsse.provider.GMJSSE.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;
       }
}
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)
   {}
}
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;
            }
        }
    }
}

注释

首先要注册国密提供者

Security.insertProviderAt(new cn.gmssl.jce.provider.GMJCE(), 1);
Security.insertProviderAt(new cn.gmssl.jsse.provider.GMJSSE(), 2);

其中要使用国密SSL来连接

String protocol = cn.gmssl.jsse.provider.GMJSSE.GMSSLv11;
String provider = cn.gmssl.jsse.provider.GMJSSE.NAME;
SSLContext ctx = SSLContext.getInstance(protocol, provider);

是不是比想象中要简单?

测试运行

>java cn.gmssl.test.SocketGet

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

GM SSL connecting...
Connected!

HTTP/1.1 200 OK
Date: Mon, 24 Aug 2020 03:45:28 GMT
Last-Modified: Sat, 27 Jun 2015 16:48:38 GMT
Accept-Ranges: bytes
Content-Length: 156
Cache-Control: max-age=300
Expires: Mon, 24 Aug 2020 03:50:28 GMT
Vary: Accept-Encoding,User-Agent
Connection: close
Content-Type: text/html
...

小结

  通过使用国密JCE和国密JSSE,Java很容易编程来使用国密SSL连接国密Web网站。www.gmssl.cn提供了全部免费的测试组件,并且支持双向国密SSL,可供学习和测试。

目录
相关文章
|
10月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
444 83
|
7月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
295 6
|
7月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
401 1
|
8月前
|
SQL Java 数据库
2025 年 Java 从零基础小白到编程高手的详细学习路线攻略
2025年Java学习路线涵盖基础语法、面向对象、数据库、JavaWeb、Spring全家桶、分布式、云原生与高并发技术,结合实战项目与源码分析,助力零基础学员系统掌握Java开发技能,从入门到精通,全面提升竞争力,顺利进阶编程高手。
1260 2
|
7月前
|
安全 前端开发 Java
从反射到方法句柄:深入探索Java动态编程的终极解决方案
从反射到方法句柄,Java 动态编程不断演进。方法句柄以强类型、低开销、易优化的特性,解决反射性能差、类型弱、安全性低等问题,结合 `invokedynamic` 成为支撑 Lambda 与动态语言的终极方案。
306 0
|
9月前
|
安全 Java 数据库连接
2025 年最新 Java 学习路线图含实操指南助你高效入门 Java 编程掌握核心技能
2025年最新Java学习路线图,涵盖基础环境搭建、核心特性(如密封类、虚拟线程)、模块化开发、响应式编程、主流框架(Spring Boot 3、Spring Security 6)、数据库操作(JPA + Hibernate 6)及微服务实战,助你掌握企业级开发技能。
1116 3
|
8月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
584 100
|
8月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
528 16
|
8月前
|
NoSQL Java 关系型数据库
超全 Java 学习路线,帮你系统掌握编程的超详细 Java 学习路线
本文为超全Java学习路线,涵盖基础语法、面向对象编程、数据结构与算法、多线程、JVM原理、主流框架(如Spring Boot)、数据库(MySQL、Redis)及项目实战等内容,助力从零基础到企业级开发高手的进阶之路。
566 1