1. 概述

在本教程中,我们将介绍 Java 平台上的安全基础,涵盖 Java 提供的安全机制和相关 API。我们还将了解如何利用这些机制构建安全的应用程序。

安全是一个非常广泛的话题,涉及多个层面。有些安全机制是 Java 语言本身内置的,比如访问控制符、类加载机制;而有些则是通过服务形式提供的,例如数据加密、安全通信、身份验证和访问控制等。

本教程不会深入探讨所有细节,但会帮助你建立对 Java 安全体系的基本理解,并掌握一些常用术语和实践技巧。

2. Java 的语言安全特性

Java 从语言层面就提供了多种安全机制,帮助开发者写出更安全的代码,也使得平台本身具备一定的安全基础:

静态类型检查:Java 是静态类型语言,减少了运行时因类型错误导致的问题
访问控制符:如 publicprivate 等,用于控制类、方法、字段的访问权限
自动内存管理:通过垃圾回收(GC)机制避免手动内存管理带来的安全风险
字节码验证:JVM 在加载类时会验证字节码,防止恶意或不安全代码执行

这些机制虽然不是万能的,但构成了 Java 安全体系的第一道防线。

3. Java 安全架构

Java 安全的核心是基于可扩展的 Provider 实现机制。每个 Provider 可以实现一个或多个安全服务,比如加密算法、密钥管理等。

Java 自带多个内置的 Provider,比如 SUN、SunJCE、SunJSSE 等。开发者也可以配置多个 Provider 并指定优先级顺序。

Java Providers

当 Java 需要某个安全服务时,会按照 Provider 的优先级依次查找,直到找到可用实现为止。这种机制使得 Java 安全框架具有良好的可扩展性。

4. 加密(Cryptography)

加密是安全通信和数据保护的核心。Java 提供了丰富的加密 API,统称为 Java Cryptographic Architecture(JCA)。

4.1. Java 加密支持

JCA 支持以下加密功能:

  • 数字签名(Digital Signatures)
  • 消息摘要(Message Digests)
  • 对称/非对称加密(Symmetric & Asymmetric Ciphers)
  • 消息认证码(MAC)
  • 密钥生成与管理

这些功能都通过 Provider 实现,Java 自带了多种常用算法的实现,如 RSA、DSA、AES 等。

4.2. 实战:密码哈希

一个常见的需求是安全地存储用户密码。不能以明文存储密码,常用做法是使用哈希算法进行单向加密。

MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] hashedPassword = md.digest("password".getBytes());

这里使用了 MessageDigest 类,通过 getInstance() 获取 SHA-1 哈希算法的实现,该实现由系统中优先级最高的 Provider 提供。

⚠️ 注意:SHA-1 已被认为不够安全,推荐使用 SHA-256 或更强的算法。

5. 公钥基础设施(PKI)

公钥基础设施(Public Key Infrastructure,PKI)是一种基于公钥加密的信任体系,用于在网络中安全地交换信息。

5.1. Java 中的 PKI 支持

Java 提供了 PKI 相关的 API,主要包括:

KeyStore:用于持久化存储密钥和信任证书,既可以是密钥库(key-store),也可以是信任库(trust-store)
CertStore:用于存储非信任证书和吊销列表,用于构建证书链

Java 自带一个名为 cacerts 的默认信任库,其中包含了主流 CA 的证书。

5.2. Java 工具支持

Java 提供了一些命令行工具来管理 PKI:

  • keytool:创建和管理 key-store 和 trust-store
  • jarsigner:对 JAR 文件进行签名和验证

5.3. 使用证书建立 SSL 连接

下面是一个使用 Java 代码加载 key-store 和 trust-store 的示例:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] keyStorePassword = "changeit".toCharArray();
try(InputStream keyStoreData = new FileInputStream("keystore.jks")){
    keyStore.load(keyStoreData, keyStorePassword);
}

实际开发中更常见的是通过 JVM 启动参数指定:

-Djavax.net.ssl.trustStore=truststore.jks 
-Djavax.net.ssl.keyStore=keystore.jks

6. 身份认证(Authentication)

身份认证是验证用户或系统身份的过程,常见方式包括用户名密码、Token、证书等。

6.1. Java 中的身份认证支持

Java 提供了 JAAS(Java Authentication and Authorization Service),支持插件式身份认证机制。核心类包括:

  • LoginContext:负责加载配置并调用合适的 LoginModule
  • LoginModule:具体的认证模块,如 Kerberos、LDAP、KeyStore 等

Java 提供了多个默认模块:

  • Krb5LoginModule:基于 Kerberos 的认证
  • JndiLoginModule:基于 LDAP 的用户名密码认证
  • KeyStoreLoginModule:基于密钥库的认证

6.2. 示例:使用 JndiLoginModule 登录

LoginContext loginContext = new LoginContext("Sample", new SampleCallbackHandler());
loginContext.login();

登录配置如下:

Sample {
  com.sun.security.auth.module.JndiLoginModule required;
};

这段配置表示使用 JndiLoginModule 作为必选的认证模块。

7. 安全通信(Secure Communication)

网络通信存在被窃听、篡改的风险。Java 提供了多种安全通信机制:

SSL/TLS:通过 SSLSocket 实现安全通信
SASL:用于客户端与服务器之间的身份验证
GSS-API/Kerberos:支持 Kerberos 协议的身份验证

7.2. 使用 SSLSocket 建立 SSL 连接

SocketFactory factory = SSLSocketFactory.getDefault();
try (Socket connection = factory.createSocket(host, port)) {
    BufferedReader input = new BufferedReader(
      new InputStreamReader(connection.getInputStream()));
    return input.readLine();
}

⚠️ 注意:使用 SSL/TLS 通信前,需要配置 key-store 和 trust-store。

8. 访问控制(Access Control)

访问控制用于保护敏感资源,防止未经授权的访问。

8.1. Java 中的访问控制机制

Java 提供了基于 SecurityManagerPolicy 的访问控制机制:

  • SecurityManager:负责执行访问控制检查
  • Policy:定义了哪些权限授予哪些代码
  • Permission:表示一个权限请求

当访问受保护资源时,SecurityManager 会检查调用栈上的权限,如果权限不足则抛出 SecurityException

8.2. Java 策略管理工具

Java 提供了 policytool 工具用于编辑策略文件。策略文件格式如下:

grant {
  permission 
    java.security.FilePermission
      "<<ALL FILES>>", "read";
};

8.3. 示例:限制文件访问

SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
    securityManager.checkPermission(
      new FilePermission("/var/logs", "read"));
}

可以通过 JVM 参数启用安全策略:

-Djava.security.manager -Djava.security.policy=/path/to/sample.policy

9. XML 签名(XML Signature)

XML 签名用于确保数据完整性,适用于 XML 或二进制数据。

9.1. Java 中的 XML 签名支持

Java 提供了 java.xml.crypto 包支持 XML 签名的生成与验证,支持三种签名类型:

  • Detached:签名与数据分离
  • Enveloping:签名包含数据
  • Enveloped:数据包含签名

9.2. 生成 XML 签名

XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);

Document document = documentBuilderFactory
  .newDocumentBuilder().parse(new FileInputStream("data.xml"));

DOMSignContext domSignContext = new DOMSignContext(
  keyEntry.getPrivateKey(), document.getDocumentElement());

XMLSignature xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo);
xmlSignature.sign(domSignContext);

上述代码为 data.xml 文件生成了一个 XML 签名,使用私钥签名,并将签名结果写入 XML 文档中。

10. 超越核心 Java 的安全框架

虽然 Java 提供了丰富的安全 API,但在实际开发中,我们通常不会直接使用这些底层 API,而是借助更高层的框架,比如:

Spring Security:目前最流行的 Java 安全框架,支持认证、授权、OAuth2、CSRF 防护等功能

Spring Security 可用于 Spring 项目,也可以在非 Spring 项目中使用。它大大简化了安全功能的实现,是构建现代 Web 应用不可或缺的工具。

11. 总结

本文简要介绍了 Java 安全体系的核心内容,包括语言特性、加密、PKI、身份认证、访问控制、XML 签名等。Java 提供了强大的安全 API,同时也支持通过 Provider 扩展机制集成第三方实现。

虽然这些 API 功能强大,但通常更适合底层开发。在实际项目中,建议使用 Spring Security 等成熟框架来快速实现安全功能。

希望本文能为你进一步探索 Java 安全机制打下坚实基础!


原始标题:The Basics of Java Security