https总结及在java中的应用

  • 概念

    http协议传输的数据都是未加密的,也就是明文的,因此使用http协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了ssl(Secure Sockets Layer)协议用于对http协议传输的数据进行加密,从而就诞生了https。简单来说,https协议是由http协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。详解:https://www.cnblogs.com/wqhwe/p/5407468.html

    https是采用非对称加密算法+对称加密算法来保证数据的安全(c 客户端 | s 服务端)。首先由c发起https请求s,s收到请求后返回证书(公钥),然后c收到公钥后随机生成一个称加密的密钥,并使用s返回的公钥加密生成的密钥,发送至s,s使用自己的私钥解密得到c的密钥,后续都是用c生成的密钥进行通信

    • 非对称加密两种用法:

      • 第一种用法:公钥加密,私钥解密。用于加解密(https中c向s发送随即密钥时使用此方式)
      • 第二种用法:私钥签名,公钥验签。用于签名
        详解:https://www.kuacg.com/22672.html
  • HTTPS中间人攻击

    https的加密传输弥补了http明文传输的安全隐患,保证了在传输过程中信息的安全,但是https本身依旧存在一个隐患,即中间人攻击。过程大致是,当我们向https服务器发送请求时,请求被劫持(DNS劫持,或者信任不安全证书),然后伪装成https服务器,接收c的请求,并返回c公钥,然后建立与c端的请求,得到c的所有请求信息,并构造一个c1伪装为c与真实的s建立通信,得到所有的返回信息,这个过程信息就会被泄露或篡改。因为c无法知道自己访问到的是真正的s,还是伪装成的s,所以https中间人攻击依然使得https存在一定的安全风险。而解决这一风险的方式就是SSL证书+CA机构

  • CA证书与keytool自制证书

    CA证书为CA机构颁发的SSL证书,keytool自制证书为使用java证书管理工具生成的证书。浏览器接受服务返回的证书后查找操作系统中已内置的受信任的证书发布机构CA,与服务器的证书中的CA匹配,查看是否为合法机构颁发,如果使用keytool自制证书则有可能被拦截,如果在keytool中将机构写成受信任的机构,浏览器从操作系统中获取CA机构的公钥,解密证书中的签名,同时使用相同的hash算法计算出服务器的证书的hash值与签名做对比,对比结果即代表证书是否受信任

  • HostnameVerifier

    在使用httpclient进行https请求时,为防止https中间人攻击http默认会使用HostnameVerifer对请求证书中的主机名与配置的主机名或请求的主机名进行匹配检验以判断证书是否为伪装

  • SSL、TLS、SSH协议

    SSH 应用层的通信加密协议,往往用于远程登录的会话;TLS(传输层安全协议)时SSL(安全套接层)标准化的结果

  • 配置

    • Spring Boot 配置
      首先生成证书:

      keytool -genkeypair -alias eairlv -keyalg RSA -keystore C:\eairlv.key
      

      然后配置:

      server:
        port: 8083
        ssl:
          key-store: eairlv.key
          key-store-type: JKS
          key-alias: eairlv
          key-store-password: eairlv.com
      http:
        port: 8084
      
    • 注入Bean:

      /**
       * server.port作为https端口
       * 如果不配置http端口的bean则默认只开启https服务
       */
      @Value("${server.port}")
      private Integer httpsPort;
      
      @Value("${http.port}")
      private Integer httpPort;
      
      @Bean
      public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
          UndertowEmbeddedServletContainerFactory undertow = new UndertowEmbeddedServletContainerFactory();
          undertow.addBuilderCustomizers((Undertow.Builder builder) ->
              builder.addHttpListener(httpPort, "0.0.0.0")
          );
          log.info("undertow http port: {}, https port: {} ", httpPort, httpsPort);
          return undertow;
      }
      
      //spring boot 2.0 UndertowEmbeddedServletContainerFactory 被剔除
      //spring boot 2.0 正确配置:
      
      /**
       * server.port作为https端口
       */
      @Value("${server.port}")
      private Integer httpsPort;
      
      @Value("${http.port}")
      private Integer httpPort;
      
      @Bean
      public UndertowServletWebServerFactory embeddedServletContainerFactory() {
          UndertowServletWebServerFactory undertow = new UndertowServletWebServerFactory();
          undertow.addBuilderCustomizers((UndertowBuilderCustomizer) builder ->
              builder.addHttpListener(httpPort, "0.0.0.0")
          );
          return undertow;
      }
      
      //增强版:
      @Bean
      public UndertowServletWebServerFactory embeddedServletContainerFactory() {
          UndertowServletWebServerFactory undertow = new UndertowServletWebServerFactory();
          undertow.addBuilderCustomizers((UndertowBuilderCustomizer) builder -> {
                          // IP配置'0.0.0.0'任意方式访问
                  builder.addHttpListener(httpPort, "0.0.0.0");
                  // 开启HTTP2
                  builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
          });
          undertow.addDeploymentInfoCustomizers(deploymentInfo -> {
              // 开启HTTP自动跳转至HTTPS
              deploymentInfo.addSecurityConstraint(new SecurityConstraint()
                      .addWebResourceCollection(new WebResourceCollection().addUrlPattern("/*"))
                      .setTransportGuaranteeType(TransportGuaranteeType.CONFIDENTIAL)
                      .setEmptyRoleSemantic(SecurityInfo.EmptyRoleSemantic.PERMIT))
                      .setConfidentialPortManager(exchange -> httpsPort);
          });
          return undertow;
      }
      
      //undertow配置http跳转https后post接口400。
      //https://stackoverflow.com/questions/37464220/springboot-undertow-redirect-post-from-http-to-https
      
    • RestTemplate配置

      @Bean
      public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
      TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
      
      SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
              .loadTrustMaterial(null, acceptingTrustStrategy)
              .build();
      
      SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
      
      CloseableHttpClient httpClient = HttpClients.custom()
              .setSSLContext(sslContext)
              .setSSLSocketFactory(csf)
              .build();
      
      HttpComponentsClientHttpRequestFactory requestFactory =
              new HttpComponentsClientHttpRequestFactory();
      
      requestFactory.setHttpClient(httpClient);
      return new RestTemplate(requestFactory);
      }
              
      //不可行方案:
      SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
      CloseableHttpClient httpClient = HttpClients.custom()
              .setSSLSocketFactory(csf)
              .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
              .build();
      
      //可行方案1:
      SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
      CloseableHttpClient httpClient = HttpClients.custom()
              .setSSLSocketFactory(csf)
              .build();
      //可行方案2:
      CloseableHttpClient httpClient = HttpClients.custom()
              .setSSLContext(sslContext)
              .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
              .build();
      
      
      //  HttpClientBuilder 
      //  933 public CloseableHttpClient build() {...} 
      //  914 if (sslSocketFactoryCopy == null) {...} 
      //  如果设置了SSLConnectionSocketFactory,CloseableHttpClient的HostnameVerifier就会使用SSLConnectionSocketFactory中的SSLHostnameVerifier,即出现不可行情况
      
    • 血案

      • httpclient访问https血案:Certificate for <localhost> doesn't match any of the subject alternative names: []
      • httpclient默认会通过HostnameVerifier验证访问主机,而我们想要访问https服务就需要对HostnameVerifier配置
      • HostnameVerifier 拥有一个DefaultHostnameVerifier,它对访问的主机名与证书中的主机名匹配校验,如:https://api.eairlv.com:8443/v2/api可访问(IE直接访问证书合法),而访问https://115.28.100.23:8443/v2/api则访问不通过(IE直接访问证书不合法)
    • 信任证书

      • 信任服务器证书,如果不配置证书信任,则httpclient默认会对证书进行校验,keytool自制的证书则无法通过校验

      • https://api.eairlv.com:8443/v2/api,IE信任而httpclient默认不信任,猜测为证书类型为DV,即信任等级较低。DV < OV < EV。DV证书仅对传输信息进行加密,并不能真正证明网站的真实身份,只是提高了伪造身份的复杂度,依然存在https中间人攻击的可能性(中间人拥有可信任机构颁发的证书,且域名一致)

        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
              .loadTrustMaterial(null, acceptingTrustStrategy)
              .build();
        或:
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new TrustManager[] { new SkipX509TrustManager() },
              new SecureRandom());
        private static class SkipX509TrustManager implements X509TrustManager {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }
        
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }
        
        }
        
    • 信任主机名(配置的主机名或请求的主机名与证书中主机名的匹配)

      重写HostnameVerifier类中的verify方法,可自行配置主机名的匹配规则,也可使用DefaultHostnameVerifier匹配请求的主机名与证书中的主机名,如果信任所有主机则只需使verify方法始终返回true

      SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
      // 据说这种方式在google play上会报不合法,需要简单的在verify中添加逻辑
      SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, (s, sslSession) -> true);
      
  • 总结

    要想防止中间人攻击证书的信任与主机名的信任需要同时开启,我之前一直也觉得只需要信任证书就行了,为什么还需要信任主机名?而如果中间人伪装的服务器也拥有CA信任证书,主机名则能够帮助客户端区分证书是否为真正受信任的CA证书。同一个域名可能对应多个可信任证书,即依然存在安全隐患,而如果希望安全性更高的话,则可采取客户端预埋证书的方式锁死证书,当客户端证书与服务端证书完全一致才能进行通信,对于证书过期问题则需要要求用户在官网(软件更新本身也会有可能被中间调包)下载证书来解决。在一般的Android程序中,连接https请求时为保证安全性可自定义规则,也可使用默认(默认更严格,自定义规则用于子域名的情况,CA通配符证书较贵),首先检验证书是否被信任与主机名是否被信任,而在浏览器中则使用默认的匹配规则以达到防止https中间人的攻击

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351