JSSE Java Secure Socket Extension

JGSS Java Generic Security Services - kerberos

SASL Simple Authentication and Security Layer

JCE 使用和 JCA 一样的架构,JCE 应该被看做是 JCA 的一部分.

JDK 中的 JCA 包含两个组件:

  • 定义密码服务以及提供支持的框架
  • 实际提供实现的 provider, 如 Sun, SunRsaSign, SunJCE

JCA 设计原则

  • 实现独立性与互操作性
  • 算法独立性与可扩展

使用密码服务,例如数字签名,消息摘要等,并不需要担心实现的细节,甚至是组成这些基础概念的的算法。完全的算法独立性是做不到的,这里的意思就是不可能完全抽象出一个接口,屏蔽使用的细节,做到算法的独立性。JCA 提供了标准化的,特定类型相关算法的 API. 并且当有多个实现时,允许开发者指定特定的实现。

算法的独立性

通过密码引擎(cryptographic engines), 以及提供这些密码引擎功能的定义类实现独立性。这些类被称为 引擎类,如 MessageDigest, Signature, KeyFactory, KeyPairGenerator, Cipher 等类

实现的独立性

使用基于 procider 的架构实现的, provider(Cryptograpic Service Provider)在这里指的是实现一个或多个密码服务(如数字签名算法,消息摘要算法等)的包或一组包。应用程序可能只是简单的从安装的众多 provider 中请求实现了某个特定服务的特定类型对象,如实现了 DSA 签名算法的 Signature 对象,当然也可以自己指定从哪个特定的 provider 中获取。provider 对应用程序的使用是透明的。

可互操作性

的事项也可以正常工作,一个 provider 提供的某个服务实现产生的密钥,可以用于另一个 provider 相同服务提供的实现。再比如证书以及证书的验证等。其实就是算法都是相同的,JCA 对输入,输出进行了标准化,所以不同的 provider 实现都要遵循相同的标准,又是实现相同的算法,当然可以互相操作了

算法的可扩展性

只要是现有引擎类支持,该引擎类类的新算法可以很容易的被添加。比如,现在有一种新的消息摘要算法,实现之后就可以提供 provider ,应用中使用相同的引擎类 MessageDigest 类获取该新算法的实例,就可以使用了。

Cryptographic Service Providers

java.security.Provider 类是所有 provider 的基类,每一个 CSP 包含了这个类的一个实例,其中包含了 provider 的名称以及所有它实现的安全服务/算法的列表。 当需要某个特定算法的实例时,JCA 框架会查询所有的 provider,如果有匹配,就会创建它的实例。

providers 的包提供了一些广为人知的密码算法的具体实现。每一个 JDK 都默认安装并配置了一个或多个 provider。额外的 provider 可以被静态或动态的安装。客户端可以配置它们的运行时环境来指定 provider 的优先级顺序。就是 provider 被搜索的顺序。

使用 JCA,可以直接请求某个特定类型的对象和特定的算法,如 MessageDigest, 使用 SHA-256 摘要算法,这会从安装的 provider 中获得一个该算法的实现:

1
md = MessageDigest.getInstance("SHA-256");

如果需要,当然也可以指定某个具体的 provider

1
md = MessageDigest.getInstance("SHA-256","providerName");

JDK 中的密码服务实现分布在几个不同的 provider 中(Sun, SunJSSE, SunJCE, SunRsaSing), 其它 Java 运行时环境可能不包含这些 provider ,所以只有在确定某个 provider 安装的情况下,才可以指定使用哪个 provider 的实现。JCA 提供了 API 可以用户查询安装的 provider 以及它们提供的安全服务/算法。JCA 使得第三方的 provider 的添加也是很容易的, 如 BouncyCastle。

Provider 具体是怎么实现的

有如下的代码,目的是获取一个 AES 密码算法的实例:

1
2
Cipher c = Cipher.getInstance("AES");
c.init(ENCRYPT_MODE, key);

具体的流程大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
调用引擎类 Cipher 的静态工厂方法 getInstance

|
\|/

JCA 框架查询每一个安装的 provider, 并获取 Provider 类的一个实例(Provider视作是提供的算法的一个数据库)

|
\|/

最终找到一个合适的 CSP(provider),并指向了继承了 CipherSPI 的实现类 AESCipher,创建 AESCipher 的实例

|
\|/

AESCIpher 实例被封装在一个新创建的 Cipher 实例, Cipher 实例返回

当调用 init 方法时,Cipher 实例会将请求委托给 AESCiphrt对象对应的 engineInit 方法

KeyStore

keystore 是一种用来管理密钥和证书的数据库。应用在需要数据认证,加密以及签名时,就可以使用 keystore,在程序中,可以通过 KeyStore 类(位于 java.security 包下)。

keystore 类型/格式

  • pkcs12 - 基于 RSA PKCS12 (Personal Information Exchange Syntax Standard)标准, JDK9 默认以及推荐的格式
  • jks - 私有格式
  • jceks - 私有格式
  • pkcs11 - 基于 RSA PKCS11 标准,支持对密码令牌的访问,如硬件安全模块和智能卡

应用可以从不同的 provider 中选择不同类型的 keystore 实现。 KeyStore 类提供了接口来访问和修改一个 keystore 中的信息。这个类代表了一个内存中密钥和证书的集合,主要用来管理两种类型的条目:

  • 密钥 - Key

    密钥类型的条目保存了非常敏感的密钥信息,必须进行保护以避免未经授权的访问。通常,是一个私密的密钥 (secret key) 或者是一个私钥(private key)以及对其公钥进行认证的证书链。私钥以及证书链被一个指定的实体使用数字签名来进行自认证。例如:软件供应商对 JAR 文件进行数字签名,作为软件发布的一部分

  • 受信任的证书 - Trusted Certificate Entry

    这种类型的条目仅包含了一个个公钥证书(从属于其它实体), 被称为受信任的证书,因为 keystore 的拥有者新人证书中的公钥确实是属于证书 subject(owner) 所标识的实体

keystore 中的每一个条目都通过一个别名 (alias) 字符串表示. 对于私钥及其关联的证书链,这些字符串区分实体进行身份验证的不同方式。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    /**
* Use openssl tool to create pkcs12 file:
* $ openssl pkcs12 -export -in con_key_crt.pem -out sub_ca.p12 -CAfile root_ca.crt -name myKeyEntry
*
* con_key_crt.pem is a simple concatenation of sub_ca_key.pem and sub_ca.crt:
* $ cat sub_ca_key.pem sub_ca.crt >> con_key_crt.pem
*
*
* @throws Exception
*/
public static void keyStoreTest() throws Exception {
try (InputStream in = new FileInputStream("D:\\idea-workspace\\sub_ca.p12")) {
KeyStore keyStore = KeyStore.getInstance("pkcs12");
// 此处的密码用于验证 keystore 的完整性
keyStore.load(in, "123456".toCharArray());

// 打印所有的 alias 别名, 此例中只有一个 myKeyEntry (openssl -name 选项)
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
System.out.println("alias:" + aliases.nextElement());
}

// 获取 RSA 私钥
Key key = keyStore.getKey("myKeyEntry", "123456".toCharArray());
System.out.println(key.getAlgorithm());

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(key, RSAPrivateKeySpec.class);
System.out.println(privateKeySpec.getModulus());

// 获取证书
Certificate certificate = keyStore.getCertificate("myKeyEntry");
System.out.println(certificate.toString());
}
}

// PKCS12 文件的生成详细内容见 PKCS 一章中的介绍,这里解析的 keystore 就是使用 openssl 创建的 PKCS12 文件, 包含了私钥及证书

Engine Classes and Algorithoms

引擎类提供了到某一个具体类型的安全服务的接口, 与 provider 及算法是独立的,提供了以下功能:

  • 密码服务操作(加密解密、数字签名,消息摘要等)

  • 密码材料(cryotographic material)的生成或转换(密钥和算法初始化参数)

  • 封装密码数据的对象(keystore 或证书),可以在抽象层的上层使用

可用的引擎类:

  • SecureRandom 用来生成随机数或伪随机数
  • MessageDigest 用来计算数据的消息摘要(hash, 单向散列函数)
  • Signature 通过密钥进行初始化,用来对数据进行签名以及验证数字签名
  • Cipher 通过密钥初始化,用来对数据进行加密,解密,有不同类型的密码算法:对称、非对称、PBE 等
  • Mac Message Authentication Codes 同样也是用来生成 hash 值的,但是要用到密钥,用来保护数据的完整性
  • KeyFactory 用来将非透明 Key 类型的密钥转换为特定密钥标准形式,反之亦然
  • SecretKeyFactory 用来将非透明 SecretKey 类型的密钥转换为特定密钥标准形式,SecretKeyFactory 是 KeyFactory 的特殊形式,专门用来生成秘密的堆成密码的密钥
  • KeyPairGenerator 用来生成一个新的公钥,私钥对,适用于特定的算法
  • KeyGeneraor 用来生成特定算法的密钥
  • KeyAgreement 使用在通信的两端,用来协商创建一个特定的密钥,使用在特定的密码操作中
  • AlgorithmParameters 用来存储的特定算法的参数,包含参数编码和解码
  • AlgorithmParameterGeneraor 用来生成适用于特定算法的一组 AlgorithmParameters
  • KeyStore 用来创建以及管理一个 keystore. 一个 keystore 是一个存储密钥的数据库,keystore 中的私钥还关联了一个与其相关的证书链,用来认证对应的公钥。keystore 可能包含来自信任实体的证书
  • CertificateFactory 用来创建公钥证书和 CRLs (Certificate Revtocation Lists) 证书撤回列表
  • CertPathBuilder 用来创建一个证书链(也称之为证书路径)
  • CertPathVlidator 用来对证书链进行验证
  • CertStore 用来从一个 repository 中检索证书和 CRLs

参考文献

[1] Java Cryptography Architecture (JCA) Reference Guide

[2] Java Security Standard Algorithm Names