Web系统安全架构—02http转https

在前面一篇文章中我们已经大概了解为了防止数据在传输中不被其他人看见,我们可以对数据进行加密,并使用CA证书进一步提高了安全性,接下来本篇文章将展示如何自建证书应用到web项目中以及常见的http转https的方法。

1. 使用JDK的keytool工具

我们可以使用JDk的keytool工具来完成密钥对和证书的创建。keytool工具使用keystore文件来存储密钥和证书(存储在服务端的公钥和私钥,证书上的公钥)

PS E:\program\java\jdk-17.0.4\bin> .\keytool.exe
密钥和证书管理工具

命令:

 -certreq            生成证书请求
 -changealias        更改条目的别名
 -delete             删除条目
 -exportcert         导出证书
 -genkeypair         生成密钥对
 -genseckey          生成密钥
 -gencert            根据证书请求生成证书
 -importcert         导入证书或证书链
 -importpass         导入口令
 -importkeystore     从其他密钥库导入一个或所有条目
 -keypasswd          更改条目的密钥口令
 -list               列出密钥库中的条目
 -printcert          打印证书内容
 -printcertreq       打印证书请求的内容
 -printcrl           打印 CRL 文件的内容
 -storepasswd        更改密钥库的存储口令
 -showinfo           显示安全相关信息

使用 "keytool -?, -h, or --help" 可输出此帮助消息
使用 "keytool -command_name --help" 可获取 command_name 的用法。
使用 -conf  选项可指定预配置的选项文件。

1、生成keystore
.\keytool.exe -genkeypair -alias test-alias -keypass 123456 -keyalg RSA -validity 365 -keystore f:\ca\test-alias.keystore -storepass 123456 -ext san=dns:localhost
注意不要忘记添加:-ext san=dns:localhost 用于添加域名信息

  • -storepass 123456 密钥库口令值
  • -keypass 123456 密钥口令值
    【详细过程】

    PS E:\program\java\jdk-17.0.4\bin> .\keytool.exe -genkeypair -alias test-alias -keypass 123456 -keyalg RSA -validity 365 -keystore f:\ca\test-alias.keystore -storepass 123456 -ext san=dns:localhost
    您的名字与姓氏是什么?
    [Unknown]:  chen
    您的组织单位名称是什么?
    [Unknown]:  chen
    您的组织名称是什么?
    [Unknown]:  chen
    您所在的城市或区域名称是什么?
    [Unknown]:  Shanghai
    您所在的省/市/自治区名称是什么?
    [Unknown]:  Shanghai
    该单位的双字母国家/地区代码是什么?
    [Unknown]:  cn
    CN=chen, OU=chen, O=chen, L=Shanghai, ST=Shanghai, C=cn是否正确?
    [否]:  y
    
    正在为以下对象生成 2,048 位RSA密钥对和自签名证书 (SHA256withRSA) (有效期为 365 天):
           CN=chen, OU=chen, O=chen, L=Shanghai, ST=Shanghai, C=cn
    PS E:\program\java\jdk-17.0.4\bin>

    2、查看keystore
    .\keytool.exe -list -v -keystore f:\ca\test-alias.keystore
    【详细过程】

    PS E:\program\java\jdk-17.0.4\bin> .\keytool.exe -list -v -keystore f:\ca\test-alias.keystore
    输入密钥库口令:
    密钥库类型: PKCS12
    密钥库提供方: SUN
    
    您的密钥库包含 1 个条目
    
    别名: test-alias
    创建日期: 2022年12月6日
    条目类型: PrivateKeyEntry
    证书链长度: 1
    证书[1]:
    所有者: CN=chen, OU=chen, O=chen, L=Shanghai, ST=Shanghai, C=cn
    发布者: CN=chen, OU=chen, O=chen, L=Shanghai, ST=Shanghai, C=cn
    序列号: 37780c1e2a0352e5
    生效时间: Tue Dec 06 15:31:39 CST 2022, 失效时间: Wed Dec 06 15:31:39 CST 2023
    证书指纹:
           SHA1: 9C:49:38:FF:AA:74:36:60:81:FF:10:88:D3:43:D7:21:21:F9:29:15
           SHA256: AE:95:CD:DB:2E:BA:45:B1:EA:66:39:D1:1A:9F:6E:AF:A4:18:A2:CE:C4:8D:B7:F1:AA:D0:52:35:82:0D:03:69
    签名算法名称: SHA256withRSA
    主体公共密钥算法: 2048 位 RSA 密钥
    版本: 3
    
    扩展:
    
    #1: ObjectId: 2.5.29.17 Criticality=false
    SubjectAlternativeName [
    DNSName: localhost
    ]
    
    #2: ObjectId: 2.5.29.14 Criticality=false
    SubjectKeyIdentifier [
    KeyIdentifier [
    0000: D4 ED F5 3C F5 3B F8 74   93 6C 9C 1F B9 2E AD 73  ...<.;.t.l.....s
    0010: 3E 1C 1D B4                                        >...
    ]
    ]
    
    
    
    *******************************************
    *******************************************

    或者.\keytool.exe -list -rfc -keystore f:\ca\test-alias.keystore
    【详细过程】

PS E:\program\java\jdk-17.0.4\bin> .\keytool.exe -list -rfc -keystore f:\ca\test-alias.keystore
输入密钥库口令:
密钥库类型: PKCS12
密钥库提供方: SUN

您的密钥库包含 1 个条目

别名: test-alias
创建日期: 2022年12月6日
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
-----BEGIN CERTIFICATE-----
MIIDeTCCAmGgAwIBAgIIN3gMHioDUuUwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UE
BhMCY24xETAPBgNVBAgTCFNoYW5naGFpMREwDwYDVQQHEwhTaGFuZ2hhaTENMAsG
A1UEChMEY2hlbjENMAsGA1UECxMEY2hlbjENMAsGA1UEAxMEY2hlbjAeFw0yMjEy
MDYwNzMxMzlaFw0yMzEyMDYwNzMxMzlaMGAxCzAJBgNVBAYTAmNuMREwDwYDVQQI
EwhTaGFuZ2hhaTERMA8GA1UEBxMIU2hhbmdoYWkxDTALBgNVBAoTBGNoZW4xDTAL
BgNVBAsTBGNoZW4xDTALBgNVBAMTBGNoZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCa/sa0kFetjFI7xqokGKp/ahzmqAYuzcpxfjM//xmZliwwZ7Fu
7uquSyT/T9k0R40+tWieXT6yHfnOZhsfhpz0KgR/k6AUdzOb3ThE87+gDDdZ0kTy
8FY8glpUXbgj+O1Ljcfi+7dbUZlxvoB5wU2jnj5sNFp+/RUc8hCDX8DVzbx1IFnZ
Kebscb8Exa9JBnYENyLMBd6MUNETnAQK1qVbsUTQ2kZQWbyLMJzUgDLwH64QBY3c
qsPmbUDn32/q2NjG8aAyBC3v3Lx7+BnPCQ2REWX92E4Lijhh2vPimRSFl5P/zuQ8
xBr6TidiQoPEo1/lwpsQCA0rhAsPjMPqJ7TlAgMBAAGjNzA1MB0GA1UdDgQWBBTU
7fU89Tv4dJNsnB+5Lq1zPhwdtDAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZI
hvcNAQELBQADggEBAAgtODXsx+sSkuudfnPqhcM3u3OcQMuG4Ut25LwfZzKoF7zo
gsQvNySQ6Ea8Bcu9yXL2iH5m9kMnDkLR5tjI4hKg3DgpIjdCINCrBRDJxn+jYrnG
z+vvXx6PlqcYo4ogoRyI1gomLK/HnrQEb70EQCQwj3Ia0WAn+YM90Mczz6t+/jBk
vKjcm5KmKxuZabM0/Vb5VHCEW3B2/SjCt+q3mob6qpMKPvjPGVQDNLIBNK421h3S
GHiomwNB0kYibJkJCalM4MiX7mF+7IsycYPijmNfMHaLtYOJkDLuFp1bLxaMIzTa
zeRSCsuLUoOk1ZxiZoQAzavXPoB7Tr0mC/EFv2g=
-----END CERTIFICATE-----


*******************************************
*******************************************

3、导出证书
.\keytool.exe -exportcert -alias test-alias -keystore f:\ca\test-alias.keystore -file f:\ca\my.cer
【详细过程】

PS E:\program\java\jdk-17.0.4\bin> .\keytool.exe -exportcert -alias test-alias -keystore f:\ca\test-alias.keystore -file f:\ca\my.cer
输入密钥库口令:
存储在文件  中的证书

2. 在web中使用

1、创建一个SpringBoot项目,添加测试接口

@RestController
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        return "hello https";
    }
}

2、启动项目

  • 访问:http://localhost:8004/hello 没问题
  • 访问:https://localhost:8004/hello 访问失败 ssl协议不支持

    3、接下来我们把前面生成的test-alias.keystore文件拷贝到resource目录下,并完成如下配置
server:
  port: 8004
  ssl:
    key-alias: test-alias
    key-store: classpath:test-alias.keystore
    key-store-type: JKS
    key-store-password: 123456
    key-password: 123456

4、访问

  • 访问http://localhost:8004/hello 出错
  • 访问:https://localhost:8004/hello 显示我们的连接不是私密连接

    直接点进去,正常:

    解决提示不安全

    在前面的测试访问中,服务端配置了密钥和证书之后,访问会提示不安全,之所以出现这种提示,是因为浏览器还没有安装我们生成的证书,接下来我们就把前面生成的证书安装。

    打开浏览器,查看证书已经按照好:

再次访问https://localhost:8004/hello (如何有问题 试试清理浏览器缓存)

http转https 简单办法

当希望用户没有指定https来访问时也调整到https访问,我们可以添加如下配置:
1、在application.yml中添加http端口为82

http-port: 82
server:
  port: 8004
  ssl:
    key-alias: test-alias
    key-store: classpath:test-alias.keystore
    key-store-type: JKS
    key-store-password: 123456
    key-password: 123456

2、添加配置

@Configuration
public class HttpToHttps {
    // https的端口
    @Value("${server.port}")
    private int sslPort;

    // http的端口
    @Value("${http-port}")
    private int httpPort;

    @Bean
    public TomcatServletWebServerFactory servletContainerFactory() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                //设置安全性约束
                SecurityConstraint securityConstraint = new SecurityConstraint();
                securityConstraint.setUserConstraint("CONFIDENTIAL");
                //设置约束条件
                SecurityCollection collection = new SecurityCollection();
                //拦截所有请求
                collection.addPattern("/*");
                securityConstraint.addCollection(collection);
                context.addConstraint(securityConstraint);
            }
        };
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        //遇到http进行转发
        connector.setScheme("http");

        //true: http使用http, https使用https;
        //false: http重定向到https;
        connector.setSecure(false);

        // 设置http端口
        connector.setPort(httpPort);

        //重定向端口号(非SSL到SSL)
        connector.setRedirectPort(sslPort);

        tomcat.addAdditionalTomcatConnectors(connector);
        return tomcat;
    }
}

3、测试
当访问http://localhost:82/hello时发现直击跳转为https://localhost:8004/hello

https转https企业方法

在真实开发中,我们一般使用Nginx来帮助我们完成http转https。

1、使用openssl来完成证书的创建
【详细过程】

PS E:\cer2\nginx> openssl genrsa -des3 -out dongbao.key 1024
Loading 'screen' into random state - done
Generating RSA private key, 1024 bit long modulus
..................++++++
....++++++
e is 65537 (0x10001)
Enter pass phrase for dongbao.key:
Verifying - Enter pass phrase for dongbao.key:
PS E:\cer2\nginx> openssl req -new -key dongbao.key -out dongbao.csr
Enter pass phrase for dongbao.key:
Loading 'screen' into random state - done
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:bj
Locality Name (eg, city) []:bj
Organization Name (eg, company) [Internet Widgits Pty Ltd]:msb
Organizational Unit Name (eg, section) []:msb
Common Name (e.g. server FQDN or YOUR name) []:cpf.com
Email Address []:[email protected]

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123456
An optional company name []:
PS E:\cer2\nginx> openssl rsa -in .\dongbao.key.src -out dongbao.key
Enter pass phrase for .\dongbao.key.src:
writing RSA key
PS E:\cer2\nginx> openssl x509 -req -days 365 -in .\dongbao.csr -signkey .\dongbao.key -out dongbao.crt
Loading 'screen' into random state - done
Signature ok
subject=/C=cn/ST=bj/L=bj/O=msb/OU=msb/CN=cpf.com/[email protected]
Getting Private key
PS E:\cer2\nginx>

2、配置conf文件

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    
    
    upstream com.cpf {
        server 127.0.0.1:8080; # 需要监听的端口名 我用的
        keepalive 64;
    }

    # HTTPS server
    #
    server {
        listen       443 ssl;
        server_name  cpf.com;
        

        ssl_certificate      E://cer2//nginx//dongbao.crt;
        ssl_certificate_key  E://cer2//nginx//dongbao.key;
        

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            proxy_pass http://com.cpf;
        }
    }
    

}

3、进行测试和访问同样可以达到效果

你可能感兴趣的:(httphttps)