在安全实践领域,TLS身份验证作为一种技术手段,通常能够保证任何用户通过证书,浏览到真实、安全的Web应用。而在此基础上发展而来的双向TLS(Two-Way TLS),则可以仅允许部分用户访问或调用目标应用。
创新互联公司主要从事做网站、成都做网站、网页设计、企业做网站、公司建网站等业务。立足成都服务集美,十余年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:028-86922220
下面,我将以示例的形式,并配合自动化脚本,依次向您展示:搭建服务器,向服务器发送未加密的hello消息,以HTTPS的方式在服务器上启用单向TLS,要求客户端通过双向TLS来标识自己,基于可信的CA(证书颁发机构)实现双向TLS,以及对HTTP客户端进行相关测试。
通过如下的统一参考页,我向社区里正在使用Apache http、Java、Kotlin、以及Scala等开发人员,提供了包含40多个http客户端的配置示例。
在处理http请求的过程中,它们可能会导致应用在初始构建时,需要花费一定的时间来下载大量依赖项。因此,我也通过GitHub - SSLContext Kickstart(https://github.com/Hakky54/sslcontext-kickstart)来简化客户端的配置。由于每一个http客户端都可能需要不同的ssl对象来启用ssl,因此代码库需要提供基本的ssl配置。
首先,我们需要做好如下准备:
如果您想在不安装任何软件的情况下,立即开始体验该项目,请点击链接,并通过在线开发环境的方式打开此项目。
由于该项目已经包含了一个maven包装器(wrapper),因此您可以在无需额外安装的情况下,运行该项目。同时,下面将涉及到的各种包含了maven包装器的命令,都已被默认包含在mvn命令中。
如果您想使用Java 8来运行该项目,则可以使用git命令:git checkout tags/java-8-compatible,来运行一个兼容的版本。有关参数的具体设置细节,请参见链接--https://github.com/Hakky54/mutual-tls-ssl/tree/java-8-compatible。
为了启动服务端,您可以在服务端的项目中运行App类的main方法,或者在终端的根目录下运行命令:cd server/ && mvn spring-boot:run,以及使用maven包装器:cd server-with-spring-boot/ && ./../mvnw spring-boot:run。
由于当前运行在默认端口8080上的服务器端是未经加密的,因此您可以在终端中使用以下curl命令:curl -i -XGET http://localhost:8080/api/hello,来调用hello:
其响应内容如下(纯文本):
- HTTP/1.1 200
- Content-Type: text/plain;charset=UTF-8
- Content-Length: 5
- Date: Sun, 11 Nov 2018 14:21:50 GMT
- Hello
您还可以使用客户端目录中所提供的客户端应用,去调用服务器。由于客户端依赖于本项目的其他组件,所以您需要先在根目录下运行mvn install或./mvnw install。
此处的客户端是基于Cucumber的集成测试。您可以通过从IDE处运行ClientRunnerIT类、或从根目录中的终端运行:cd client/ && mvn exec:java、亦或使用maven的包装器命令:cd client/ && ./../mvnw exec:java,来启动之。您可以在客户端项目的测试资源中,通过Hello.feature文件,来获悉集成测试的具体步骤。
为了同时运行服务器和客户端中的方法,您可以在根目录中使用命令:mvn clean verify,或使用maven的包装器:./mvnw clean verify。如果服务端与客户端同处一台服务器,那么客户端会默认向localhost发送请求;如果它们在不同的主机上运行,您需要为客户端提供带有VM参数:-Durl=http://[HOST]:[PORT]的定制化的url。
下面,我们来讨论如何通过启用TLS,来保护服务器端。您可以通过将如下所需的属性(YAML),添加到名为application.yml的应用属性文件中来实现:
- server:
- port: 8443
- ssl:
- enabled: true
在此,您可能会对为何将端口设置为 8443表示疑惑。其原因在于:带有https的tomcat服务的约定端口为8443,而对于http则是8080。虽然我们可以使用端口8080进行https连接,但这并不是一种推荐的做法。具体有关端口约定的详细信息,请参阅维基百科的链接。
您可以通过重启服务器,来生效那些对于应用的更改。当然,您也可能会收到异常信息:IllegalArgumentException: Resource location must not be null。该消息的产生,是因为服务器需要带有服务器证书的密钥库,以确保与外界的安全连接。如果您提供的VM参数为:Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake,那么服务器可以为您提供更多的信息。
显然,为了解决此问题,您需要创建一个带有服务器公钥和私钥的密钥库。其中的公钥可与用户共享,以便加密彼此之间的通信;而服务器的私钥则可用来解密。值得注意的是,我们绝对不可以共享服务器的私钥,以避免被其他人用来破解截获到的通信,进而获悉被加密的通信内容。
因此,若要创建带有公钥和私钥的密钥库,请在终端中执行以下命令(纯文本):
- keytool -v -genkeypair -dname "CN=Hakan,OU=Amsterdam,O=Thunderberry,C=NL" -keystore shared-server-resources/src/main/resources/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias server -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -ext SubjectAlternativeName:c=DNS:localhost,DNS:raspberrypi.local,IP:12 0.1
为了告知服务器密钥库的位置,以及具体的密码,请将如下YAML内容粘贴到您的application.yml文件中:
- server:
- port: 8443
- ssl:
- enabled: true
- key-store: classpath:identity.jks
- key-password: secret
- key-store-password: secret
至此,您已成功启用了服务器和客户端之间的TLS加密连接。您可以尝试着使用curl命令:curl -i --insecure -v -XGET https://localhost:8443/api/hello,去调用服务器。
当您在ClientRunnerIT类中运行客户端时,您可能会看到一条错误消息:java.net.ConnectException: Connection refused (Connection refused)。从字面上看,它是指客户端试图向服务器建立连接,可以被拒绝了。其深层原因是:客户端试图使用的是端口8080,而服务器只在端口8443上处于活跃状态。因此,您需要进行如下修改,并将其应用到Constants类中,即从:
- private static final String DEFAULT_SERVER_URL = "http://localhost:8080";
改为:
- private static final String DEFAULT_SERVER_URL = "https://localhost:8443";
在完成修改之后,让我们再次运行客户端。您会看到另一条消息:“javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target”。这意味着客户端希望通过HTTPS进行通信,但是在它握手的过程中,收到了无法识别的服务器证书。可见,您还需要创建一个包含了各种受信任证书的信任库,以方便客户端在SSL握手过程中,将收到的证书与其信任库里的证书内容进行比较。如果相匹配的话,则可以继续SSL的握手过程。当然,在创建信任库之前,您需要事先获得服务器的证书。
您可以使用如下命令,导出服务器的证书:
- keytool -v -exportcert -file shared-server-resources/src/main/resources/server.cer -alias server -keystore shared-server-resources/src/main/resources/identity.jks -storepass secret -rfc
接着,您可以为客户端创建一个信任库,并使用如下命令导入服务器的证书:
- keytool -v -importcert -file shared-server-resources/src/main/resources/server.cer -alias server -keystore client/src/test/resources/truststore.jks -storepass secret -noprompt
为了让客户端知晓信任库的存在,您还需要告知其信任库的正确位置、密码、以及身份验证已启用。您可以在客户端的application.yml文件中,提供如下属性:
- client:
- ssl:
- one-way-authentication-enabled: true
- two-way-authentication-enabled: false
- trust-store: truststore.jks
- trust-store-password: secret
接下来,服务器端需要验证客户端的身份,以判断其是否可信。其实现方式为:通过client-auth属性放入服务器的application.yml中,以告知服务器去验证客户端。
- server:
- port: 8443
- ssl:
- enabled: true
- key-store: classpath:identity.jks
- key-password: secret
- key-store-password: secret
- client-auth: need
当然,如果您直接运行它,则会因为客户端根本没有证书,而产生错误消息:javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate(无效的证书信息)。因此,我们需要通过如下命令,来创建证书:
- keytool -v -genkeypair -dname "CN=Suleyman,OU=Altindag,O=Altindag,C=NL" -keystore client/src/test/resources/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -别名客户端 -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth
同时,您还需要为服务器创建一个信任库。不过,在创建信任库之前,您需要通过如下命令获取客户端的证书:
- keytool -v -exportcert -file client/src/test/resources/client.cer -alias client -keystore client/src/test/resources/identity.jks -storepass secret -rfc
下一步便是使用客户端的证书,来创建服务器的信任库:
- keytool -v -importcert -file client/src/test/resources/client.cer -alias client -keystore shared-server-resources/src/main/resources/truststore.jks -storepass secret -noprompt
同样,为了让客户端获悉该密钥库的存在,您还需要告知其信任库的正确位置、密码、以及身份验证已启用。您可以在客户端的application.yml文件中,提供如下属性:
- client:
- ssl:
- one-way-authentication-enabled: false
- two-way-authentication-enabled: true
- key-store: identity.jks
- key-password: secret
- key-store-password: secret
- trust-store: truststore.jks
- trust-store-password: secret
对应地,为了让服务器知晓新创建的信任库,我们需要将当前属性替换为以下属性:
- server:
- port: 8443
- ssl:
- enabled: true
- key-store: classpath:identity.jks
- key-password: secret
- key-store-password: secret
- trust-store: classpath:truststore.jks
- trust-store-password: secret
- client-auth: need
至此,您已完成了双向TLS的安装。如果再次运行客户端,您将会发现客户端能够以安全的方式,从服务器端接收到hello消息了。
有了前面的基础,我们便可以采用基于可信CA的双向(mutual)认证了。我们首先来看看它的优缺点:
其具体实现步骤如下:
通常,您需要向某个已有的证书颁发机构,提供自己的证书以获取其签名。下面,我们将创建一个自己的CA,并用它去签发客户端和服务器的证书。
- keytool -v -genkeypair -dname "CN=Root-CA,OU=Certificate Authority,O=Thunderberry,C=NL" -keystore root-ca/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias root-ca -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,keyCertSign -ext BasicConstraints=ca:true,PathLen:3
当然,您也可以使用存储库默认提供的那个,具体请参阅identity.jks。
为了签发证书,您需要通过如下命令,提供一个证书签名请求 (.csr) 文件。其中,服务器的证书签名请求为:
- keytool -v -genkeypair -dname "CN=Root-CA,OU=Certificate Authority,O=Thunderberry,C=NL" -keystore root-ca/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias root-ca -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,keyCertSign -ext BasicConstraints=ca:true,PathLen:3
而客户端的证书签名请求为:
- keytool -v -certreq -file client/src/test/resources/client.csr -keystore client/src/test/resources/identity.jks -alias client -keypass secret -storepass secret -keyalg rsa
签发客户证书:
- keytool -v -gencert -infile client/src/test/resources/client.csr -outfile client/src/test/resources/client-signed.cer -keystore root-ca/identity.jks -storepass secret -alias root-ca -validity 3650 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -rfc
签发服务器证书:
- keytool -v -gencert -infile shared-server-resources/src/main/resources/server.csr -outfile shared-server-resources/src/main/resources/server-signed.cer -keystore root-ca/identity.jks -storepass secret -alias root-ca -validity 3650 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -ext SubjectAlternativeName:c=DNS:localhost,DNS:raspberrypi.local,IP:127.0.0.1 -rfc
由于我们无法直接用密钥工具(keytool)去导入已签名的证书,因此我们需要将由CA签发的证书存储到identity.jks中。先导出CA证书:
- keytool -v -exportcert-文件root-ca / root-ca.pem -alias root-ca -keystore root-ca / identity.jks -storepass secret -rfc
然后是客户端:
- keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore client/src/test/resources/identity.jks -storepass secret -noprompt
- keytool -v -importcert -file client/src/test/resources/client-signed.cer -alias client -keystore client/src/test/resources/identity.jks -storepass secret
- keytool -v -delete -alias root-ca -keystore client/src/test/resources/identity.jks -storepass secret
最后是服务器端:
- keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore shared-server-resources/src/main/resources/identity.jks -storepass secret -noprompt
- keytool -v -importcert -file shared-server-resources/src/main/resources/server-signed.cer -alias server -keystore shared-server-resources/src/main/resources/identity.jks -storepass secret
- keytool -v -delete -alias root-ca -keystore shared-server-resources/src/main/resources/identity.jks -storepass secret
为了将客户端和服务器配置为仅信任某个CA,我们需要通过将CA证书导入客户端和服务器的信任库来实现。其中在客户端,我们可以使用如下操作命令:
- keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore client/src/test/resources/truststore.jks -storepass secret -noprompt
在服务器端则为:
- keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore shared-server-resources/src/main/resources/truststore.jks -storepass secret -noprompt
同时,由于信任库仍包含客户端和服务器的原有特定证书,因此我们需要将其删除。其中在客户端,我们可以使用如下操作命令:
- keytool -v -delete -alias server -keystore client/src/test/resources/truststore.jks -storepass secret
在服务器端则为:
- keytool -v -delete -alias client -keystore shared-server-resources/src/main/resources/truststore.jks -storepass secret
至此,如果您再次运行客户端,将能够顺利通过测试。客户端将会接收到来自服务器的hello消息,而且其中的证书是由该CA所颁发的。
其实,您还可以使用该项目的脚本目录里的各种脚本,来自动化执行上述步骤。例如,对于单向认证而言,可以输入:./configure-one-way-authentication;而对于双向认证而言,则可以输入:./configure-two-way-authentication-by-trusting-each-other my-company-name;对于通过可信CA进行的双向身份验证,可以输入:./configure-two-way-authentication-by-trusting-root-ca my-company-name。
下面是已经通过测试的客户端列表。您可以在ClientConfig类中找到基于纯Java的http客户端配置。该服务目录包含了单个的http客户端请求示例。其中,基于Kotlin和Scala的http客户端配置是作为嵌套类被包含在内的。而且,所有客户端示例都使用的是在SSLConfig类中创建的相同的ssl基本配置。
当前题目:如何轻松地设置双向TLS保护应用程序安全
浏览路径:http://www.csdahua.cn/qtweb/news33/303333.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网