国密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,可供学习和测试。

目录
相关文章
|
5天前
|
设计模式 安全 Java
Java编程中的单例模式深入剖析
【10月更文挑战第21天】在Java的世界里,单例模式是设计模式中一个常见而又强大的存在。它确保了一个类只有一个实例,并提供一个全局访问点。本文将深入探讨如何正确实现单例模式,包括常见的实现方式、优缺点分析以及最佳实践,同时也会通过实际代码示例来加深理解。无论你是Java新手还是资深开发者,这篇文章都将为你提供宝贵的见解和技巧。
89 65
|
1天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
8 3
|
3天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
4天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
1天前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
7 2
|
2天前
|
Java
Java中的多线程编程:从基础到实践
本文深入探讨Java多线程编程,首先介绍多线程的基本概念和重要性,接着详细讲解如何在Java中创建和管理线程,最后通过实例演示多线程的实际应用。文章旨在帮助读者理解多线程的核心原理,掌握基本的多线程操作,并能够在实际项目中灵活运用多线程技术。
|
2天前
|
Java 程序员 开发者
Java编程中的异常处理艺术
【10月更文挑战第24天】在Java的世界里,代码就像一场精心编排的舞蹈,每一个动作都要精准无误。但就像最完美的舞者也可能踩错一个步伐一样,我们的程序偶尔也会遇到意外——这就是所谓的异常。本文将带你走进Java的异常处理机制,从基本的try-catch语句到高级的异常链追踪,让你学会如何优雅地处理这些不请自来的“客人”。
|
3天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
19 3
|
7天前
|
Java API 调度
Java中的多线程编程:理解与实践
本文旨在为读者提供对Java多线程编程的深入理解,包括其基本概念、实现方式以及常见问题的解决方案。通过阅读本文,读者将能够掌握Java多线程编程的核心知识,提高自己在并发编程方面的技能。
|
5天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。