java - 如何在Spring Boot中配置ssl?

标签 java spring-boot ssl keytool jks

我需要在 Spring Boot 中使用 Restful 服务支持 ssl。虽然服务应用程序启动成功,但我无法在Chrome(版本:68.0.3440.106)中访问该页面。错误信息:ERR_SSL_VERSION_OR_CIPHER_MISMATCH

捕获的错误消息图像: error message

项目的pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>test</groupId>
  <artifactId>spring-restful-server-https</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

配置文件(密码已被故意屏蔽):

server.port=8443
server.ssl.key-store=classpath:shq.jks
server.ssl.key-store-password=******
server.ssl.key-password=******

Controller 类:

package test.spring_restful_server_https;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));
    }
}

我使用java keytool生成shq.jks文件。
key 工具命令:

keytool -genkeypair -alias shq -keystore shq.jks

执行后:

keytool -list -keystore shq.jks -v

jks文件信息(我的环境是中文,所以我翻译了信息并放在括号中):

密钥库类型(keystore type): JKS
密钥库提供方(keystore provider): SUN

您的密钥库包含 1 个条目(your keystore contains 1 entry)

别名(alias name): shq
创建日期(creation date): 2018-8-16
条目类型(entry type): PrivateKeyEntry
证书链长度(certificate chain length): 1
证书(certificate)[1]:
所有者(owner): CN=shq, OU=home, O=home, L=shanghai, ST=shanghai, C=cn
发布者(issuer): CN=shq, OU=home, O=home, L=shanghai, ST=shanghai, C=cn
序列号(serial number): 279abe96
有效期开始日期(valid from): Thu Aug 16 21:02:31 CST 2018, 截止日期(until): Wed Nov 14 21:02:31 CST 2018
证书指纹(certificate fingerprints):
         MD5: 78:E1:91:21:04:AC:38:F1:0B:30:D8:51:69:FD:BE:28
         SHA1: 8A:D5:1C:26:69:6B:5C:50:75:A6:E8:BE:66:2F:58:01:68:8F:78:1A
         SHA256: 74:0F:F1:0D:59:75:93:3B:BD:55:75:3D:F1:E8:23:17:CE:5D:C0:14:63:0D:D9:53:54:29:C2:C4:70:5A:82:C0
         签名算法名称(signature algorithm name): SHA1withDSA
         版本(version): 3

扩展(extensions):

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: AF 9F 16 35 95 36 FD 13   0C EA 19 F8 A0 D0 E3 6F  ...5.6.........o
0010: A6 CB BB EE                                        ....
]
]

最佳答案

keytool -genkeypair 默认情况下会创建 DSA key 和(自签名)证书,并且 Chrome 显然不再支持任何使用 DSA 的密码套件(SSL/TLS 将其称为 DSS)对于歇斯底里的葡萄干)。我知道过去确实如此,因为我曾经那样使用过它,但我确定最后一次是很久以前的事了。要修复这个问题,请添加 -keyalg rsa。此外,如果您没有使用当前或最新版本的 Java,keytool 可能默认为 1024 位,这已被官方禁止(不安全)好几年了(尽管我非常确定 Chrome 还没有对手动输入的证书强制执行);为了安全起见,还要添加 -keysize 2048

但这还不够。 keytool -genkeypair 默认情况下会创建一个不带扩展名的证书,特别是不带“SAN”扩展名(主题备用名称)。大约自本世纪初以来,SAN 就被推荐用于 HTTPS 证书,并且从去年开始,Chrome 就开始要求使用它。幸运的是,如果您add -ext SAN=type:value[,...],最新版本的keytool 可以生成SAN。 。但请注意 SAN 中的值必须匹配用于连接的名称:如果您告诉浏览器https://127.0.0.1:port/ 如您的帖子所示,证书的 SAN 必须包含 IP:127.0.0.1 的条目;像 DNS:myhost.foo 这样的域名条目是不同的名称,并且不匹配,因此不会被接受,即使 myhost.foo 解析为地址 127.0.0.1 。 SAN 支持多个条目,因此您可以根据需要拥有一个或多个 IP 和/或一个或多个 DNS。 (如果您使用其他浏览器,您可以暂时忽略 SAN,如果您确实使主题字段“CN”(通用名称)匹配(并且它只能保存一个值),但我不会打赌还要持续多久。)

如果你不知道,因为你没有提到它,一个自签名的证书,比如由 keytool 生成的(或由许多其他东西,如 openssl req -new -x509(您可以在网络上找到它,包括 Stack 上)默认情况下将被视为不受信任且无效。您必须将其添加到浏览器的信任库中,然后它才会受到信任。由于 Chrome 使用底层系统的信任库,因此您可以通过 Chrome 或通过其他方式来执行此操作,具体方式取决于您未识别的系统。 如果您要从其他客户端访问此内容(我假设您会这样做),则不同客户端使用的信任库通常取决于客户端,对此您没有说什么。

添加:看起来这件事发生在 2016 年;他们删除了所有 DHE key 交换,作为防止 DHE 过小(又名 Logjam)的粗略方法,并且唯一的 DSA (DSS) key 交换需要 DHE。 https://bugs.chromium.org/p/chromium/issues/detail?id=619194

关于java - 如何在Spring Boot中配置ssl?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51878511/

相关文章:

javascript - 强制将 HTTP 重定向到 HTTPS

java - Name 和 FriendlyName 有什么区别?

java - 如何使用 AbstractProcessor 访问另一个注解中的注解 - 而不是反射

java - 为什么javac要创建一个额外的类?

java - 无法通过环境变量配置 Spring Boot/Spring Kafka SSL

java - 如何找出特定端点是否在 Spring Boot 中启用了 keepalive?

java - 如何从 Java 中的二进制文件反序列化 ArrayList 数据?

docker - 在当前项目和插件组中找不到前缀 'docker' 的插件

php - 验证时出现 Google 日历 SSL 错误

asp.net - 新域名的 URL 重写