The Basics of Java Security – Java安全的基础知识

最后修改: 2019年 8月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll go through the basics of security on the Java platform. We’ll also focus on what’s available to us for writing secure applications.

在本教程中,我们将了解Java平台上的安全基础知识。我们还将重点介绍我们可以用来编写安全应用程序的内容。

Security is a vast topic that encompasses many areas. Some of these are part of the language itself, like access modifiers and class loaders. Furthermore, others are available as services, which include data encryption, secure communication, authentication, and authorization, to a name a few.

安全是一个广泛的话题,包含了许多领域。其中一些是语言本身的一部分,如访问修改器和类加载器。此外,还有一些是作为服务提供的,其中包括数据加密、安全通信、认证和授权,仅举几例。

Therefore, it’s not practical to gain meaningful insight into all of these in this tutorial. However, we’ll try to gain at least a meaningful vocabulary.

因此,在本教程中对所有这些都获得有意义的洞察力是不实际的。然而,我们将尝试至少获得一个有意义的词汇。

2. Language Features

2.语言特点

Above all, security in Java begins right at the level of language features. This allows us to write secure code, as well as benefit from many implicit security features:

最重要的是,Java中的安全始于语言特性层面。这使我们能够编写安全的代码,并从许多隐含的安全特性中受益。

  • Static Data Typing: Java is a statically typed language, which reduces the possibilities of run-time detection of type-related errors
  • Access Modifiers: Java allows us to use different access modifiers like public and private to control access to fields, methods, and classes
  • Automatic Memory Management: Java has garbage-collection based memory management, which frees developers from managing this manually
  • Bytecode Verification: Java is a compiled language, which means it converts code into platform-agnostic bytecode, and runtime verifies every bytecode it loads for execution

This is not a complete list of security features that Java provides, but it’s good enough to give us some assurance!

这并不是Java所提供的安全功能的完整清单,但它足以给我们带来一些保证!

3. Security Architecture in Java

3.Java中的安全架构

Before we begin to explore specific areas, let’s spend some time understanding the core architecture of security in Java.

在我们开始探索具体领域之前,让我们花些时间了解Java中安全的核心架构。

The core principles of security in Java are driven by interoperable and extensible Provider implementations. A particular implementation of Provider may implement some or all of the security services.

Java中安全的核心原则是由可互操作和可扩展的Provider实现驱动的。Provider的特定实现可以实现部分或全部安全服务。

For example, some of the typical services a Provider may implement are:

例如,一个提供者可能实现的一些典型服务是。

  • Cryptographic Algorithms (such as DSA, RSA, or SHA-256)
  • Key generation, conversion, and management facilities (such as for algorithm-specific keys)

Java ships with many built-in providers. Also, it’s possible for an application to configure multiple providers with an order of preference.

Java有许多内置的提供者。此外,一个应用程序有可能配置多个具有偏好顺序的提供者。

Java Providers

 

Consequently, the provider framework in Java searches for a specific implementation of a service in all providers in the order of preference set on them.

因此,Java中的提供者框架在所有提供者中按照对其设定的优先级顺序搜索服务的具体实现。

Moreover, it’s always possible to implement custom providers with pluggable security functions in this architecture.

此外,在这个架构中,总是可以实现具有可插拔安全功能的自定义提供者。

4. Cryptography

4.密码学

Cryptography is the cornerstone of security features in general and in Java. This refers to tools and techniques for secure communication in the presence of adversaries.

密码学是一般情况下和Java中安全功能的基石。这指的是在对手面前进行安全通信的工具和技术

4.1. Java Cryptography

4.1.Java密码学

The Java Cryptographic Architecture (JCA) provides a framework to access and implement cryptographic functionalities in Java, including:

Java Cryptographic Architecture (JCA)提供了一个在Java中访问和实现加密功能的框架,包括。

Most importantly, Java makes use of Provider-based implementations for cryptographic functions.

最重要的是,Java利用基于Provider的实现来实现加密功能。

Moreover, Java includes built-in providers for commonly used cryptographic algorithms like RSA, DSA, and AES, to name a few. We can use these algorithms to add security to data in rest, in use, or in motion.

此外,Java还包括常用加密算法的内置提供者,如RSA、DSA和AES等等。我们可以使用这些算法来为静止的、使用中的或运动中的数据增加安全性

4.2. Cryptography in Practice

4.2.实践中的密码学

A very common use case in applications is to store user passwords. We use this for authentication at a later point in time. Now, it’s obvious that storing plain text passwords compromises security.

应用程序中一个非常常见的用例是存储用户密码。我们用它来在以后的时间点上进行认证。现在,很明显,存储纯文本密码会损害安全性。

So, one solution is to scramble the passwords in such a way that the process is repeatable, yet only one-way. This process is known as the cryptographic hash function, and SHA1 is one such popular algorithm.

因此,一个解决方案是以这样一种方式来窜改密码,即这个过程是可重复的,但只是单向的。这个过程被称为加密散列函数,而SHA1就是这样一种流行的算法。

So, let’s see how we can do this in Java:

那么,让我们看看如何在Java中做到这一点。

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

Here, MessageDigest is a cryptographic service that we are interested in. We’re using the method getInstance() to request this service from any of the available security providers.

这里,MessageDigest是我们感兴趣的一个加密服务。我们使用getInstance()方法,从任何一个可用的安全提供者那里请求这个服务

5. Public Key Infrastructure

5.公钥基础设施

Public Key Infrastructure (PKI) refers to the setup that enables the secure exchange of information over the network using public-key encryption. This setup relies on trust that is built between the parties involved in the communication. This trust is based on digital certificates issued by a neutral and trusted authority known as a Certificate Authority (CA).

公钥基础设施(PKI)是指使用公钥加密在网络上安全交换信息的设置。这种设置依赖于参与通信的各方之间建立的信任。这种信任的基础是由一个被称为证书颁发机构(CA)的中立和可信的机构颁发的数字证书。

5.1. PKI Support in Java

5.1.Java中的PKI支持

Java platform has APIs to facilitate the creation, storage, and validation of digital certificates:

Java平台有一些API来促进数字证书的创建、存储和验证

  • KeyStore: Java provides the KeyStore class for persistent storage of cryptographic keys and trusted certificates. Here, KeyStore can represent both key-store and trust-store files. These files have similar content but vary in their usage.
  • CertStore: Additionally, Java has the CertStore class, which represents a public repository of potentially untrusted certificates and revocation lists. We need to retrieve certificates and revocation lists for certificate path building amongst other usages.

Java has a built-in trust-store called “cacerts” that contains certificates for well known CAs.

Java有一个名为 “cacerts “的内置信任库,其中包含知名CA的证书。

5.2. Java Tools for PKI

5.2.用于PKI的Java工具

Java has some really handy tools to facilitate trusted communication:

Java有一些非常方便的工具来促进可信的通信。

  • There is a built-in tool called “keytool” to create and manage key-store and trust-store
  • There is also another tool “jarsigner” that we can use to sign and verify JAR files

5.3. Working with Certificates in Java

5.3.在Java中使用证书

Let’s see how we can work with certificates in Java to establish a secure connection using SSL. A mutually authenticated SSL connection requires us to do two things:

让我们看看我们如何在Java中使用证书来建立一个使用SSL的安全连接。一个相互认证的SSL连接需要我们做两件事。

  • Present Certificate — We need to present a valid certificate to another party in the communication. For that, we need to load the key-store file, where we must have our public keys:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] keyStorePassword = "changeit".toCharArray();
try(InputStream keyStoreData = new FileInputStream("keystore.jks")){
    keyStore.load(keyStoreData, keyStorePassword);
}
  • Verify Certificate — We also need to verify the certificate presented by another party in the communication. For this we need to load the trust-store, where we must have previously trusted certificates from other parties:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// Load the trust-store from filesystem as before

We rarely have to do this programmatically and normally pass system parameters to Java at runtime:

我们很少需要以编程方式进行,通常在运行时将系统参数传递给Java。

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

6. Authentication

6.认证

Authentication is the process of verifying the presented identity of a user or machine based on additional data like password, token, or a variety of other credentials available today.

认证是根据额外的数据(如密码、令牌或当今可用的各种其他凭证)验证用户或机器所呈现的身份的过程。

6.1. Authentication in Java

6.1.Java中的认证

Java APIs makes use of pluggable login modules to provide different and often multiple authentication mechanisms to applications. LoginContext provides this abstraction, which in turn refers to configuration and loads an appropriate LoginModule.

Java APIs利用可插拔的登录模块来为应用程序提供不同的、通常是多种认证机制。LoginContext提供了这种抽象,它反过来引用配置并加载适当的LoginModule

While multiple providers make available their login modules, Java has some default ones available for use:

虽然多个供应商提供了他们的登录模块,但Java有一些默认的模块可供使用

  • Krb5LoginModule, for Kerberos-based authentication
  • JndiLoginModule, for username and password-based authentication backed by an LDAP store
  • KeyStoreLoginModule, for cryptographic key-based authentication

6.2. Login by Example

6.2.通过实例登录

One of the most common mechanisms of authentication is the username and password. Let’s see how we can achieve this through JndiLoginModule.

最常见的认证机制之一是用户名和密码。让我们看看如何通过JndiLoginModule实现这一目标。

This module is responsible for getting the username and password from a user and verifying it against a directory service configured in JNDI:

这个模块负责从一个用户那里获得用户名和密码,并根据JNDI中配置的目录服务进行验证。

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

Here, we are using an instance of LoginContext to perform the login. LoginContext takes the name of an entry in the login configuration — in this case, it’s “Sample”. Also, we have to provide an instance of CallbackHandler, using the LoginModule that interacts with the user for details like username and password.

在这里,我们正在使用LoginContext的一个实例来执行登录LoginContext需要登录配置中的一个条目名称–在本例中,它是 “Sample”。此外,我们必须提供一个CallbackHandler的实例,使用LoginModule与用户互动,以获取用户名和密码等细节。

Let’s take a look at our login configuration:

让我们看一下我们的登录配置。

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

Simple enough, it suggests that we’re using JndiLoginModule as a mandatory LoginModule.

很简单,它表明我们正在使用JndiLoginModule作为一个强制性的LoginModule

7. Secure Communication

7.安全通信

Communication over the network is vulnerable to many attack vectors. For instance, someone may tap into the network and read our data packets as they’re being transferred. Over the years, the industry has established many protocols to secure this communication.

网络上的通信容易受到许多攻击媒介的影响。例如,有人可能侵入网络,在我们的数据包传输过程中读取它们。多年来,该行业已经建立了许多协议来确保这种通信的安全。

7.1. Java Support for Secure Communication

7.1.Java对安全通信的支持

Java provides APIs to secure network communication with encryption, message integrity, and both client and server authentication:

Java提供了APIs,以通过加密、消息完整性以及客户端和服务器验证来确保网络通信

  • SSL/TLS: SSL and its successor, TLS, provide security over untrusted network communication through data encryption and public-key infrastructure. Java provides support of SSL/TLS through SSLSocket defined in the package “java.security.ssl“.
  • SASL: Simple Authentication and Security Layer (SASL) is a standard for authentication between client and server. Java supports SASL as part of the package “java.security.sasl“.
  • GGS-API/Kerberos: Generic Security Service API (GSS-API) offers uniform access to security services over a variety of security mechanisms like Kerberos v5. Java supports GSS-API as part of the package “java.security.jgss“.

7.2. SSL Communication in Action

7.2.行动中的SSL通信

Let’s now see how we can open a secure connection with other parties in Java using SSLSocket:

现在让我们看看如何在Java中使用SSLSocket与其他各方打开一个安全连接。

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

Here, we are using SSLSocketFactory to create SSLSocket. As part of this, we can set optional parameters like cipher suites and which protocol to use.

这里,我们使用SSLSocketFactory来创建SSLSocket。作为其中的一部分,我们可以设置可选的参数,如密码套件和使用的协议。

For this to work properly, we must have created and set our key-store and trust-store as we saw earlier.

为了使其正常工作,我们必须创建并设置我们的密钥存储和信任存储,正如我们之前看到的那样。

8. Access Control

8.访问控制

Access Control refers to protecting sensitive resources like a filesystem or codebase from unwarranted access. This is typically achieved by restricting access to such resources.

访问控制是指保护敏感资源,如文件系统或代码库,使其不被无端访问。这通常是通过限制对这些资源的访问来实现的。

8.1. Access Control in Java

8.1.Java中的访问控制

We can achieve access control in Java using classes Policy and Permission mediated through the SecurityManager class. SecurityManager is part of the “java.lang” package and is responsible for enforcing access control checks in Java.

我们可以在Java中实现访问控制 使用通过SecurityManager类介导的PolicyPermissionSecurityManager是”java.lang“包的一部分,负责在Java中执行访问控制检查。

When the class loader loads a class in the runtime, it automatically grants some default permissions to the class encapsulated in the Permission object. Beyond these default permissions, we can grant more leverage to a class through security policies. These are represented by the class Policy.

当类加载器在运行时加载一个类时,它会自动授予封装在Permission对象中的该类一些默认权限。除了这些默认权限外,我们还可以通过安全策略为类授予更多的权限。这些由Policy类来表示。

During the sequence of code execution, if the runtime encounters a request for a protected resource, SecurityManager verifies the requested Permission against the installed Policy through the call stack. Consequently, it either grants permission or throws SecurityException.

在代码执行序列中,如果运行时遇到对受保护资源的请求,SecurityManager会通过调用堆栈根据已安装的Policy验证请求的Permission。因此,它要么授予权限,要么抛出SecurityException

8.2. Java Tools for Policy

8.2.政策的Java工具

Java has a default implementation of Policy that reads authorization data from the properties file. However, the policy entries in these policy files have to be in a specific format.

Java有一个Policy的默认实现,它从属性文件中读取授权数据。然而,这些策略文件中的策略条目必须采用特定的格式。

Java ships with “policytool”, a graphical utility to compose policy files.

Java带有 “policytool”,这是一个用于编写策略文件的图形化工具。

8.3. Access Control Through Example

8.3.通过实例进行访问控制

Let’s see how we can restrict access to a resource like a file in Java:

让我们看看如何在Java中限制对文件等资源的访问。

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

Here, we’re using SecurityManager to validate our read request for a file, wrapped in FilePermission.

在这里,我们使用SecurityManager来验证我们对一个文件的读取请求,用FilePermission来包装。

But, SecurityManager delegates this request to AccessController. AccessController internally makes use of the installed Policy to arrive at a decision.

但是,SecurityManager将此请求委托给AccessControllerAccessController在内部利用已安装的Policy来达成决定。

Let’s see an example of the policy file:

让我们看看政策文件的一个例子。

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

We are essentially granting read permission to all files for everyone. But, we can provide much more fine-grained control through security policies.

我们基本上是授予每个人对所有文件的读取权限。但是,我们可以通过安全策略提供更细化的控制

It’s worth noting that a SecurityManager might not be installed by default in Java. We can ensure this by always starting Java with the parameter:

值得注意的是,SecurityManager可能不会被默认安装在Java中。我们可以通过总是用参数启动Java来确保这一点。

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

9. XML Signature

9.XML签名

XML signatures are useful in securing data and provide data integrity. W3C provides recommendations for governance of XML Signature. We can use XML signature to secure data of any type, like binary data.

XML签名在保障数据安全和提供数据完整性方面非常有用。W3C为XML签名的治理提供了建议。我们可以使用XML签名来保护任何类型的数据,如二进制数据。

9.1. XML Signature in Java

9.1 Java中的XML签名

Java API supports generating and validating XML signatures as per the recommended guidelines. Java XML Digital Signature API is encapsulated in the package “java.xml.crypto“.

Java API 支持生成和验证 XML 签名,符合推荐的准则。Java XML数字签名API被封装在”java.xml.crypto“包中。

The signature itself is just an XML document. XML signatures can be of three types:

签名本身只是一个XML文档。XML签名可以有三种类型。

  • Detached: This type of signature is over the data that is external to the Signature element
  • Enveloping: This type of signature is over the data that is internal to the Signature element
  • Enveloped: This type of signature is over the data that contains the Signature element itself

Certainly, Java supports creating and verifying all the above types of XML signatures.

当然,Java支持创建和验证上述所有类型的XML签名。

9.2. Creating an XML Signature

9.2.创建一个XML签名

Now, we’ll roll up our sleeves and generate an XML signature for our data. For instance, we may be about to send an XML document over the network. Hence, we would want our recipient to be able to verify its integrity.

现在,我们将卷起袖子,为我们的数据生成一个XML签名。例如,我们可能要在网络上发送一个XML文档。因此,我们希望我们的收件人能够验证其完整性

So, let’s see how we can achieve this in Java:

因此,让我们看看如何在Java中实现这一目标。

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);

To clarify, we’re generating an XML signature for our data present in the file “data.xml”. Meanwhile, there are a few things to note about this piece of code:

澄清一下,我们正在为文件“data.xml “中的数据生成一个XML签名。同时,关于这段代码有几件事需要注意。

  • Firstly, XMLSignatureFactory is the factory class for generating XML signatures
  • XMLSigntaure requires a SignedInfo object over which it calculates the signature
  • XMLSigntaure also needs KeyInfo, which encapsulates the signing key and certificate
  • Finally, XMLSignature signs the document using the private key encapsulated as DOMSignContext

As a result, the XML document will now contain the Signature element, which can be used to verify its integrity.

因此,XML文档现在将包含签名元素,它可以用来验证其完整性。

10. Security Beyond Core Java

10.超越核心Java的安全

As we have seen by now, the Java platform provides a lot of the necessary functionality to write secure applications. However, sometimes, these are quite low-level and not directly applicable to, for example, the standard security mechanism on the web.

正如我们现在所看到的,Java平台提供了很多编写安全应用程序的必要功能。然而,有时候,这些都是相当低级的,并不直接适用于,例如,网络上的标准安全机制。

For example, when working on our system, we generally don’t want to have to read the full OAuth RFC and implement that ourselves. We often need quicker, higher-level ways to achieve security. This is where application frameworks come into the picture – these help us achieve our objective with much less boilerplate code.

例如,在我们的系统中工作时,我们通常不希望必须阅读完整的OAuth RFC并自己实现它。我们通常需要更快、更高层次的方式来实现安全。这就是应用框架的作用–这些框架帮助我们以更少的模板代码实现我们的目标。

And, on the Java platform – generally that means Spring Security. The framework is part of the Spring ecosystem, but it can actually be used outside of pure Spring application.

而且,在Java平台上–一般来说,这意味着Spring Security。该框架是Spring生态系统的一部分,但它实际上可以在纯Spring应用之外使用。

In simple terms, it helps is achieve authentication, authorization and other security features in a simple, declarative, high-level manner.

简单地说,它有助于以一种简单的、声明性的、高水平的方式实现认证、授权和其他安全功能。

Of course, Spring Security is extensively covered in a series of tutorials, as well as in a guided way, in the Learn Spring Security course.

当然,在一系列教程中,以及在Learn Spring Security课程中以指导方式广泛地介绍了Spring安全。

11. Conclusion

11.结语

In short, in this tutorial, we went through the high-level architecture of security in Java. Also, we understood how Java provides us with implementations of some of the standard cryptographic services.

简而言之,在本教程中,我们了解了Java中安全的高级架构。此外,我们还了解了Java是如何为我们提供一些标准加密服务的实现的。

We also saw some of the common patterns that we can apply to achieve extensible and pluggable security in areas like authentication and access control.

我们还看到了一些常见的模式,我们可以应用这些模式在认证和访问控制等领域实现可扩展和可插拔的安全。

To sum up, this just provides us with a sneak peek into the security features of Java. Consequently, each of the areas discussed in this tutorial merits further exploration. But hopefully, we should have enough insight to get started in this direction!

总而言之,这只是为我们提供了一个偷窥Java安全特性的机会。因此,本教程中讨论的每一个领域都值得进一步探索。但是,希望我们应该有足够的洞察力,在这个方向上开始行动