【ssl认证、证书】java中的ssl语法API说明(SSLContext)、与keytool 工具的联系

这篇具有很好参考价值的文章主要介绍了【ssl认证、证书】java中的ssl语法API说明(SSLContext)、与keytool 工具的联系。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


相关文章:
//-----------Java SSL begin----------------------
【ssl认证、证书】SSL双向认证和SSL单向认证的区别(示意图)
【ssl认证、证书】java中的ssl语法API说明(SSLContext)、与keytool 工具的联系
【ssl认证、证书】SSL双向认证java实战、keytool创建证书
【ssl认证、证书】Wireshark抓包分析
【ssl认证、证书】 查看keystore文件内容
//------------Java SSL end--------------------------

//-----------下面的是CA证书和openssl相关的知识--------------
【ssl认证、证书】TLS/SSL双向认证概念、openssl genrsa示例
【ssl认证、证书】openssl genrsa 命令详解
【ssl认证、证书】SSL 证书基本概念、证书格式、openssl和keytool的区别

1. 前言

在java中,我们经常需要使用SSL/TLS连接,这种连接是加密连接,https之类的基于TCP/IP协议的应用层协议加密传输,也经常基于这种加密连接,这种连接jdk底层已经提供了默认的实现,并且是基于jdk中的keystroe证书库实现的认证。

在常用的http连接组件(如okhttp,httpcomponent)中,底层连接一般也是通过SocketFactroy建立的连接,通常不需要我们专门设置SocketFactory,使用的是jdk内置的默认实现:

SocketFactory sf = SocketFactory.getDefault();

默认的SocketFactory实现ssl连接时,是基于{JAVA_HOME}/jre/lib/security/cacerts这个默认的已信任证书库的实现,因此当遇到证书库中不存在的证书时,就会出现ssl连接异常的问题。

这个默认的已信任证书库如何使用,参见下文的<2.3.1.1 使用默认的证书库>章节

此时我们通常会通过创建一个忽略认证校验的SocketFactory,并设置为默认实现:

SocketFactory.setDefault(sf);

或者将不信任的证书安装到{JAVA_HOME}/jre/lib/security/cacerts中解决问题。

但是实际上,设置默认值,影响了整个系统的安全性,因为此时意味着所有证书都信任,容易遭到攻击,而将证书安装到证书库中,又改变了标准的jdk,此时相当于应用和jdk绑定了,不便于迁移或者jdk更新。

因此,为了解决上述两个问题,我们应该针对特定的服务使用特定的SocketFactory,这样就可以将忽略证书校验的影响最小化。

java中的ssl语法与keytool 工具的联系

标准的SSL协议在Java的落地,具体来说是用通过后面提到的SSLContext实现的,并且必须按照JKS (Java KeyStone)格式提供证书,而keytool 正是生成JKS格式文件的工具,即使你用openssl工具创建了证书,也需要先利用相应的工具,将其转化为JKS格式,然后才能在Java中使用。

2. SSLContext的体系

结合 【ssl认证、证书】SSL双向认证java实现、keytool创建证书 中的示例来看

java中所有基于TCP/IP协议的SSL/TLS网络编程本质都是基于SSLSocket这个对象,因此这个问题的核心在于如何让组件的代码使用我们定制化的方式创建SSLSocket对象。

常见的网络编程组件都是通过SSLSocketFactory这个接口来创建SSLSocket的,因此要定制网络编程组件创建的SSLSocket,我们只需要给组件提供定制的SSLSocketFactory即可。

以http组件为例,常见的http组件都支持我们设置定制化的SSLSocketFactory对象来代替默认实现。

现在的问题在于,我们如何创建一个定制化的SSLSocketFactory对象?

我们先来看看在jdk中的SSL体系结构:
java ssl证书连接,Java,web,ssl,java

  • ① 通信核心类——SSLSocket和SSLServerSocket。对于使用过socket进行通信开发的朋友比较好理解,它们对应的就是Socket与ServerSocket,只是表示实现了SSL协议的Socket和ServerSocket,同时它们也是Socket与ServerSocket的子类。SSLSocket负责的事情包括设置加密套件、管理SSL会话、处理握手结束时间、设置客户端模式或服务器模式。

  • ② 客户端与服务器端Socket工厂——SSLSocketFactory和SSLServerSocketFactory。在设计模式中工厂模式是专门用于生产出需要的实例,这里也是把SSLSocket、SSLServerSocket对象创建的工作交给这两个工厂类。

  • ③ SSL会话——SSLSession。安全通信握手过程需要一个会话,为了提高通信的效率,SSL协议允许多个SSLSocket共享同一个SSL会话,在同一个会话中,只有第一个打开的SSLSocket需要进行SSL握手,负责生成密钥及交换密钥,其余SSLSocket都共享密钥信息。

  • ④ SSL上下文——SSLContext。它是对整个SSL/TLS协议的封装,表示了安全套接字协议的实现。主要负责设置安全通信过程中的各种信息,例如跟证书相关的信息。并且负责构建SSLSocketFactory、SSLServerSocketFactory和SSLEngine等工厂类。

  • ⑤ SSL非阻塞引擎——SSLEngine。假如你要进行NIO通信,那么将使用这个类,它让通过过程支持非阻塞的安全通信。

  • ⑥ 密钥管理器——KeyManager。此接口负责选择用于证实自己身份的安全证书,发给通信另一方。KeyManager对象由KeyManagerFactory工厂类生成。

  • ⑦ 信任管理器——TrustManager。此接口负责判断决定是否信任对方的安全证书,TrustManager对象由TrustManagerFactory工厂类生成。

图我们可以看出,要创建一个SSLSocketFactory我们需要通过SSLContext这个对象来创建,而SSLContext这个对象又依赖TrustManager和KeyManager这两个对象,这两个对象都依赖KeyStore这个对象。

因此我们需要从KeyStore对象开始创建。

2.1 KeyStore

KeyStore对象实际上就是一个证书库,我们前面提到的jdk内置证书库{JAVA_HOME}/jre/lib/security/cacerts加载到内存后,最终就是形成KeyStore对象。

KeyStore 是一个仓库,但是后文会提到KeyManager(入参是一个包含本端私钥+本端证书的KeyStore)和TrustManager(入参是一个包含对端证书的KeyStore),也就是说根据不同的协议,KeyStore 会实际存储不同的内容

证书库的创建方式有多种:

2.1.1 通过证书库文件创建:

String key="{JAVA_HOME}/jre/lib/security/cacerts";
String password = "123456";
KeyStore keystore=KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(key),password.toCharArray());

在上面的代码中,我们创建了一个使用{JAVA_HOME}/jre/lib/security/cacerts文件作为证书库源的证书库对象。

证书库文件可以通过jdk提供的keytool命令自行生成,这里不展开讲。

2.1.2 随机生成自签名证书库

随机生成的证书没有存储到实际物理目录,直接被导入到keyStore中:

String algorithm = "RSA";
String algorithmSign = "MD5WithRSA";
try {
    CertAndKeyGen cak = new CertAndKeyGen(algorithm,algorithmSign);
    cak.generate(1024);
    X500Name subject = new X500Name("CN=localhost,o=netease");
    X509Certificate certificate = cak.getSelfCertificate(subject,10);
    
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    
    keyStore.setKeyEntry("local", cak.getPrivateKey(),password.toCharArray(), new Certificate[]{certificate});
    return keyStore;
}catch (Exception e){
    throw new RuntimeException(e);
}

这里我们使用RSA算法创建了一个证书生成器,并且指定随机生成密钥对,长度为1024,接着使用密钥对生成一个证书对象X509Certificate。

然后我们通过keyStore.load(null);初始化了一个空的证书库,最后将我们随机生成的证书设置到证书库中:

keyStore.setKeyEntry("local", cak.getPrivateKey(),password.toCharArray(), new Certificate[]{certificate});

2.2 KeyManager

KeyManager是密钥管理器,主要用于验证证书有效性的,密钥管理器对象通常有两种方式创建。

这里需要强调一点,KeyManager的入参是一个包含本端私钥+本端证书的KeyStore,当然,由于访问keyStone需要密码,因此,也是比较安全的;而
TrustManager的入参是一个仅包含对端证书的KeyStore

2.2.1 KeyManagerFactory工厂创建:

String password =123456“;
KeyStore keystore = null; // 省略keystore创建过程,可以参考前面的小结
KeyManagerFactory kmf=KeyManagerFactory.getInstance("SunX509");  
kmf.init(keystore,password.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();

这段代码中,我们获取了一个SunX509的密钥管理器工厂,并使用自己指定的keystore和密码进行初始化,初始化之后使用工厂创建了一个密钥管理器数组。

这个密钥管理器数组即是根据我们的密钥库生成的,支持校验密钥库中的证书的密钥管理器数组。

2.2.2 自己创建一个密钥管理器数组:

这种方式相对少用,因为密钥管理器主要的功能是讲自己的密钥传给连接的对方进行校验,通常我们只需要使用KeyManagerFactory配合KeyStore创建即可直接使用jdk的默认实现,很少有需要自己定制密钥管理器的场景。

下面是 X509KeyManager接口的匿名抽象类实现,在此,你可以知道有那些抽象方法:

KeyManager[] kms = new KeyManager[]{
        new X509KeyManager() {
            @Override
            public String[] getClientAliases(String s, Principal[] principals) {
                return new String[0];
            }

            @Override
            public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
                return null;
            }

            @Override
            public String[] getServerAliases(String s, Principal[] principals) {
                return new String[0];
            }

            @Override
            public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
                return null;
            }

            @Override
            public X509Certificate[] getCertificateChain(String s) {
                return new X509Certificate[0];
            }

            @Override
            public PrivateKey getPrivateKey(String s) {
                return null;
            }
        }
};

2.3 TrustManager

TrustManager是校验管理器,一般用于校验连接的对方发来的证书是否有效,通常也会有两种方式创建.

TrustManager保存服务端的授权证书的副本,功能类似白名单,用于判断对端传送过来的证书是否合法,只有在tks的证书判定有效,否则直接拒绝连接。

2.3.1 使用TrustManagerFactory创建:

String password =123456“;
KeyStore keystore = null; // 省略keystore创建过程,可以参考前面的小结
TrustManagerFactory tmf=TrustManagerFactory.getInstance("SunX509");  
tmf.init(keystore);   //执行init后,就可以调用下文的  tmf.getTrustManagers()方法 
TrustManager[] tms = tmf.getTrustManagers();

这里我们使用TrustManagerFactory的实例和KeyStore创建了一个认证管理器数组,这个数组可以用于校验连接的另一边发来的所有证书,并且只信任KeyStore中信任的证书。

2.3.1.1 使用默认的证书库

tmf.init(null);时,会自动使用 {JAVA_HOME}/jre/lib/security/cacerts 作为信任证书库

public javax.net.ssl.TrustManagerFactory  {
 public final void init(KeyStore ks)  throws KeyStoreException{
 factorySpi.engineInit(ks);  //跟进去
}

实现类 TrustManagerFactoryImpl:

public   sun.security.ssl.TrustManagerFactoryImpl  {
    protected void engineInit(KeyStore ks) throws KeyStoreException{
     if (ks == null) {
            try {
                trustManager = getInstance(TrustStoreManager.getTrustedCerts());   //跟进去TrustStoreManager.getTrustedCerts()
                ......
}
public     TrustStoreManager  {
	public static Set<X509Certificate> getTrustedCerts() throws Exception {
        return tam.getTrustedCerts(TrustStoreDescriptor.createInstance());  //TrustStoreDescriptor.createInstance()
    }
public  TrustStoreDescriptor   {

    private static final String defaultStore =
                defaultStorePath + fileSep + "cacerts";     //{JAVA_HOME}/jre/lib/security/cacerts

   static TrustStoreDescriptor createInstance() {
             return AccessController.doPrivileged(
                    new PrivilegedAction<TrustStoreDescriptor>() {

                @Override
                public TrustStoreDescriptor run() {
                    // Get the system properties for trust store.
                    String storePropName = System.getProperty(
                            "javax.net.ssl.trustStore", jsseDefaultStore);
                    String storePropType = System.getProperty(
                            "javax.net.ssl.trustStoreType",
                            KeyStore.getDefaultType());
                    String storePropProvider = System.getProperty(
                            "javax.net.ssl.trustStoreProvider", "");
                    String storePropPassword = System.getProperty(
                            "javax.net.ssl.trustStorePassword", "");     
   。。。。
    if (!"NONE".equals(storePropName)) {
                        String[] fileNames =
                                new String[] {storePropName, defaultStore};               //defaultStore是前文定义的变量                

2.3.2 自行创建一个信任全部证书的认证管理器数据:

通过下面的X509ExtendedTrustManager接口的匿名抽象类,我们可以知道有抽象方法用于对端证书的校验:

TrustManager[] tms = new TrustManager[]{
    X509TrustManager trustManager = new X509ExtendedTrustManager(){
        public X509Certificate[] getAcceptedIssuers(){ return null; }
        public void checkClientTrusted(X509Certificate[] certs, String authType){}
        public void checkServerTrusted(X509Certificate[] certs, String authType){}
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {}
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {}
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {}
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {}
    }
}

这里我们直接创建了一个X509TrustManager对象作为认证管理器数组的元素,并且这个对象对所有的证书都不进行校验(没抛异常,即认为校验通过),即表示信任所有的证书。

这个是我们常见的针对自签名证书的信任的实现方式,当然这是不安全的。

2.4 SSLContext

终于到了SSLContext对象,前面我们创建的所有对象,最终都是为了构造一个加密传输的上下文环境,这个环境就是SSLContext。

现在我们可以轻松的创建一个SSLContext对象:

KeyManager[] kms = null; // 省略创建过程
TrustManager[] tms = null; // 省略创建过程
SecureRandom sr = new SecureRandom();
SSLContext ssl=SSLContext.getInstance("TLS");  
ssl.init(kms,tms,sr);  

ssl对象通过密钥管理器,认证管理器和随机数SecureRandom即可初始化完成,这里SecureRandom也可以为null。

值得注意的是,在这个安全上下文中,其实密钥管理器和认证管理器并不是必须的。

当你的程序作为客户端向服务端发起连接时(如http客户端连接http服务端),如果服务器没有要求认证客户端证书(通常http服务端不会要求认证客户端证书)时,可以不传kms,此时只需要你自己觉得你的程序是否需要认证服务端证书,此时你只需要传tms决定如何认证服务端证书即可:

ssl.init(null,tms,sr);  

当你的程序作为服务端被客户端发起连接时,假设你不要求客户端证书认证(http服务端大多数不会要求认证客户端证书),此时只需要给客户端发送你的证书即可,客户端可能会校验:

ssl.init(kms,null,sr);

当你的程序和另一个程序连接的过程,双方都要求证书认证时,才需要同时传递kms和tms:

ssl.init(kms,tms,sr);

2.5 SSLSocketFactory

现在所有的SSL上下文都构建完成,SSLSocketFactory只需要直接通过SSLContext获取即可:

SSLContext ssl=null;//省略创建过程
SSLSocketFactory sf = ssl.getSocketFactory();

到这里我们就创建了一个完全定制化的SSLSocketFactory,只要传给对应的组件作为socket工厂即可。

2.6 HttpsURLConnection

常见的第三方http组件确实可以通过自定义SSLSocketFactory实现指定请求的忽略证书,而非全局忽略证书,但是jdk内置的HttpsURLConnection就比较坑了,不支持针对连接单独制定socket工厂,而是直接使用SSLSocketFactory.getDefault()获取默认的socket工厂,这就导致我们只能通过全局设置忽略证书的方式实现忽略证书校验。

不过这也不是完全无法改变,查看SSLSocketFactory.getDefault()源码可以发现如下代码:


  public static synchronized ServerSocketFactory getDefault() {
	String var0 = getSecurityProperty("ssl.SocketFactory.provider");
	if (var0 != null) {
	    log("setting up default SSLSocketFactory");
	
	    try {
	        Class var1 = null;
	
	        try {
	            var1 = Class.forName(var0);
	        } catch (ClassNotFoundException var5) {
	            ClassLoader var3 = ClassLoader.getSystemClassLoader();
	            if (var3 != null) {
	                var1 = var3.loadClass(var0);
	            }
	        }
	
	        log("class " + var0 + " is loaded");
	        SSLSocketFactory var2 = (SSLSocketFactory)var1.newInstance();
	        log("instantiated an instance of class " + var0);
	        theFactory = var2;
	        return var2;
	    } catch (Exception var6) {
	        log("SSLSocketFactory instantiation failed: " + var6.toString());
	        theFactory = new DefaultSSLSocketFactory(var6);
	        return theFactory;
	    }
	}

这里看到String var0 = getSecurityProperty("ssl.SocketFactory.provider");提供了一种通过系统属性指定socket工厂实现类的方式,因此我们可以通过ThreadLocal和自定义实现类的方式来使得针对单个连接使用我们的定制socket工厂:

先创建一个自定义的工厂:

public class CustomSocketFactory implement SSLSocketFactory{

}

通过threadLocal () :

//...
ThreadLocal<Boolean> useCustom = new InheritableThreadLocal<>();

HttpsURLConnect conn = null;//省略创建过程
String old = Security.getProperty("ssl.SocketFactory.provider");
try{
    if(useCustom.get()){
        // 指定socket工厂
        Security.setProperty("ssl.SocketFactory.provider", CustomSocketFactory.class.getName());
    }
    conn.connect();
}finally{
    // 恢复socket工厂
    Security.setProperty("ssl.SocketFactory.provider", old);  //貌似也不对,万一是并发的请求,
														       //那么修改该环境变量的,没有恢复的话,会影响其他的线程读取该环境变量
}
//...

在本文中所有的类都基于jdk8的实现,没有使用外部依赖,如果使用外部依赖的话,可能实现起来会更加简单。

参考

java中的ssl连接
Java SSL实现使用详解
SSL/TLS通信及其在Java中的实践文章来源地址https://www.toymoban.com/news/detail-686295.html

到了这里,关于【ssl认证、证书】java中的ssl语法API说明(SSLContext)、与keytool 工具的联系的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手机SSL证书认证失败是什么意思?

    手机SSL证书认证失败是指在使用手机设备浏览网站时,由于SSL证书的认证问题,导致无法建立安全的加密连接。本文将详细介绍手机 SSL证书 认证失败的含义、可能的原因以及解决方法,帮助用户了解并解决该问题,以确保手机端浏览的数据传输安全与隐私保护。 手机SSL证书

    2024年02月09日
    浏览(39)
  • 基于SSL/TLS双向安全连接设备CA证书认证

    小伙伴儿们,如果觉得文章干货满满,欢迎加入公众号【编程识堂】,更多干货等你们来哦! 设备证书是由CA根证书签发给客户端设备使用的数字证书,用于客户端和服务端连接时,服务端对客户端进行安全认证。认证通过后服务端和客户端可基于证书内的加密密钥进行安全

    2024年01月20日
    浏览(47)
  • Openfeign和okHttp的https请求忽略ssl证书认证

    在通过feign和okhttp请求外部接口时,出现了以下问题: Servlet.service() for servlet [dispatcherServlet] in context with path [/xxxx] threw exception [Request processing failed; nested exception is feign.RetryableException: java.security.cert.CertificateException: No subject alternative DNS name matching www.xx.xx.cn found. executing GET htt

    2024年02月07日
    浏览(51)
  • 【ssl认证、证书】openssl genrsa 命令详解、生成和查看密钥内容

    相关文章: //-----------Java SSL begin---------------------- 【ssl认证、证书】SSL双向认证和SSL单向认证的区别(示意图) 【ssl认证、证书】java中的ssl语法API说明(SSLContext)、与keytool 工具的联系 【ssl认证、证书】SSL双向认证java实战、keytool创建证书 【ssl认证、证书】Wireshark抓包分析 【s

    2024年02月11日
    浏览(38)
  • 玩儿转EMQ X:SSL篇之认证证书及单片机实现

    玩儿转EMQ X:SSL篇之认证证书及单片机实现 在使用EMQ X搭建MQTT Broker时,通常需要开启SSL加密以保障通信安全。而为了确保SSL的安全性,我们需要使用认证证书进行双向身份验证。本文将介绍如何在EMQ X中配置SSL认证证书,并给出如何在单片机上实现MQTT SSL连接的示例代码。 一

    2024年02月07日
    浏览(34)
  • 域名 SSL 证书信息解析 API 数据接口

    域名 SSL 证书信息解析 API 数据接口 网络工具,提供域名 SSL 证书信息解析,多信息查询,毫秒级响应。 提供域名 SSL 证书信息解析; 最完整 SSL 属性信息解析; 支持多种元素信息抽取,包括主题的可辨识名称、颁发者的可辨识名称、序列号、有效起始日期、有效结束日期、

    2024年02月21日
    浏览(37)
  • SpringBoot整合自签名SSL证书,转变HTTPS安全访问(单向认证服务端)

    HTTP 具有相当优秀和方便的一面,然而 HTTP 并非只有好的一面,事物皆具两面性,它也是有不足之处的。例如: 通信使用明文(不加密),内容可能会被窃听。 不验证通信方的身份,因此有可能会遭遇伪装。 无法证明报文的完整性,所以有可能会遭篡改等。 因HTTP有存在通信

    2024年02月06日
    浏览(64)
  • 解决:pip安装包时出现SSL证书认证问题-SSLError-SSLZeroReturnError等

    安装时出现SSL证书认证问题,如下图: 1、可以通过下载包的whl文件放在本地进行安装 2、将源换为http开头的链接,不用https协议的链接 1、通过whl文件进行本地安装 下载.whl文件:可以直接在官网下载包,要注意下载的包是否适配。本地安装可以参考该文章 2、换源进行下载

    2024年02月14日
    浏览(47)
  • API签名认证的说明及实现

    请思考一个重要的问题:如果我们为开发者提供了一个接口,却对调用者一无所知。假设我们的服务器只能允许100个人同时调用接口。如果有攻击者疯狂的请求这个接口,那将极其危险。一方面这可能会损害我们的安全性,另一方面也可能耗尽服务器性能,影响正常用户的使

    2024年02月10日
    浏览(32)
  • SSL中的CA证书

    目录 一、CA概述 二、数据加密 三、身份认证         SSL如何保证网络通信的安全和数据的完整性呢?就是采用了两种手段: 身份认证 和 数据加密 。身份认证就需要用到CA证书。         CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心。

    2024年04月12日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包