JCA是平台的一个主要部分,包含一个“Provider”体系结构和一组用于数字签名,消息摘要(哈希),证书和证书验证,加密(对称/非对称块/流密码),密钥生成 管理和安全随机数生成等等。
JCA包含两个软件组件:
- 定义和支持Provider为其提供实现的加密服务的框架。 这个框架包含了诸如java.security,javax.crypto,javax.crypto.spec和javax.crypto.interfaces等软件包。
- Sun,SunRsaSign,SunJCE等实际包含了实际的加密实现的提供商。
JDK的其他安全库
JDK中提供的其他密码通信库使用JCA提供程序体系结构,但在别处进行了介绍。
- Java安全套接字扩展(JSSE)提供对安全套接字层(SSL)和传输层安全性(TLS)实现的访问。
- Java通用安全服务(JGSS)(通过Kerberos)API以及简单身份验证和安全层(SASL)也可用于在通信应用程序之间安全地交换消息。
术语
- JCE(Java Cryptography Extension),在早期JDK版本中,由于受美国的密码出口条例约束,Java中涉及加解密功能的API被限制出口,所以Java中安全组件被分成了两部分: 不含加密功能的JCA(Java Cryptography Architecture )和含加密功能的JCE(Java Cryptography Extension)。现在JCE已经捆绑在JDK中,所以,这里JCE是JCA的一部分。
设计原则
- 实现独立性和互操作性
- 算法独立性和可扩展性
JCA提供标准化的、算法特定的API来实现上面两个原则。
- 通过各种引擎类来实现不同的功能,实现算法独立性(例如MessageDigest,Signature,KeyFactory,KeyPairGenerator和Cipher类)
- 使用Provider(提供者)的体系结构来达到“实现独立性”,程序可以指定使用特定服务提供者的算法实现。
- 实现互操作性意味着各种实现可以相互协作,使用彼此的密钥,或者验证彼此的签名。 这就意味着,对于相同的算法,由一个提供者生成的密钥可以被另一个提供者使用,并且由一个提供者生成的签名可以被另一个提供者验证。
核心包
JCA相关安全API软件包:
- java.security
- javax.crypto
provider 提供者
provider提供者,这里的全称是 Cryptographic Service Provider (CSP),是指实现一个或多个密码服务(如数字签名算法,消息摘要算法和密钥转换服务)的包或一组包。 每个JDK安装都默认安装并配置了一个或多个provider包。用户可以静态或动态添加其他provider。
JDK中的加密库出于历史原因,由几个不同的提供者实现:
- Sun
- SunJSSE
- SunJCE
- SunRsaSign
java.security.Provider是所有安全提供程序的基类。
有两者指定Provider的方式:
- 根据默认的优先顺序自动获取提供者
- 指定特定的Provider
Provider实现原理
JCA使用引擎类作为路由来实现Provider的关联,实现算法独立性。
- 引擎类:每个引擎类,都有一个对应的抽象SPI类,它定义了每个加密服务提供者算法必须实现的方法。
- SPI(服务提供者接口):每个SPI类的名称与相应的引擎类相同,接着是Spi。 例如,Signature引擎类提供对数字签名算法的功能的访问。 实际的提供者实现是在SignatureSpi的子类中提供的。 应用程序调用引擎类的API方法,在实际的实现中又调用SPI方法。
下面的案例,展示了引擎类的使用,获取Provider对象实例的过程:
例如
import javax.crypto.*;
Cipher c = Cipher.getInstance("AES");
c.init(ENCRYPT_MODE, key);
- 这里的应用程序需要一个“AES”算法的javax.crypto.Cipher实例,并不关心使用哪个提供者。
- 应用程序调用Cipher引擎类的getInstance()工厂方法,然后请求JCA框架查找支持“AES”的第一个提供程序实例。
- 该框架会咨询每个已安装的提供者,并获取提供者类的提供者实例。框架搜索每个提供者,最终在CSP3中找到合适的条目。
- 这个数据库入口指向扩展CipherSpi的实现类com.foo.AESCipher,因此适用于Cipher引擎类。创建一个com.foo.AESCipher的实例,并将其封装在一个新创建的javax.crypto.Cipher实例中,该实例返回给应用程序。
- 当应用程序现在对Cipher实例执行init()操作时,Cipher引擎类将请求路由到com.foo.AESCipher类中相应的engineInit()支持方法中。
- 理解引擎类,SPI(服务提供接口类)、SPI实现类
如上案例,Cipher是一个引擎类,提供加解密的服务。
而实际定义加解密功能的类是CipherSpi。
提供者CSP3的com.foo.AESCipher类是一个SPI实现类,实现了CipherSpi接口。
引擎类
引擎类为特定类型的密码服务提供接口,而不依赖于特定的密码算法或提供者。 引擎需要提供:
- 密码操作(加密,数字签名,消息摘要等),
- 发生器或密码材料的转换器(密钥和算法参数),或
- 对象(密钥库或证书)封装了密码数据,可以在更高的抽象层使用。
以下引擎类是可用的:
- SecureRandom:用于生成随机或伪随机数字。
- MessageDigest:用于计算指定数据的消息摘要(散列)。
- Signature:使用密钥初始化,这些签名用于签署数据并验证数字签
- Cipher:用密钥初始化,用于加密/解密数据。存在各种类型的算法:对称批量加密(例如AES),非对称加密(例如RSA)和基于密码的加密(例如PBE)。
- Message Authentication Codes(MAC):与MessageDigests一样,它们也会生成散列值,但是首先使用密钥初始化以保护消息的完整性。
KeyFactory:用于将Key类型的现有不透明密钥转换为密钥规范(底层密钥材料的透明表示),反之亦然。
SecretKeyFactory:用于将SecretKey类型的现有不透明加密密钥转换为密钥规范(底层密钥材料的透明表示),反之亦然。 SecretKeyFactorys是专门的KeyFactorys,只能创建密钥(对称)。
KeyPairGenerator:用于生成一对适用于指定算法的公钥和私钥。
KeyGenerator:用于生成与指定算法一起使用的新密钥。
KeyAgreement:由两方或多方使用,商定和建立一个特定的密钥,用于特定的密码操作。
AlgorithmParameters:用于存储特定算法的参数,包括参数编码和解码。
AlgorithmParameterGenerator:用于生成适合于指定算法的一组AlgorithmParameters。
KeyStore:用于创建和管理密钥库。密钥库是密钥的数据库。密钥库中的私钥具有与其关联的证书链,用于验证相应的公钥。密钥库还包含来自可信实体的证书。
CertificateFactory:用于创建公钥证书和证书吊销列表(CRL)。
CertPathBuilder:用于构建证书链(也称为证书路径)。
CertPathValidator:用于验证证书链。
CertStore:用于从存储库中检索证书和CRL。
注意:生成器可以创建具有全新内容的对象,而工厂只能从现有材料(例如编码)中创建对象。
1. Provider类
- 指定Provider类
在调用引擎类的getInstance方法来请求和实例化实现实例时,指定想要实现的所需算法的名称以及可选的Provider(或提供者类)的名称。 - 安装Providers
有两种方式安装Provider:在classpath的任何位置放置一个包含Provider类的zip或JAR文件;如果将提供程序置于标准扩展目录中,则该提供程序将被视为已安装的扩展程序 - 注册Provider
静态注册:在配置文件JAVA_HOME\lib\security\java.security中注册security.provider.n=masterClassName
动态注册:调用Security类中的addProvider或insertProviderAt方法。 - Provider类方法
public String getName()
public double getVersion()
public String getInfo()
2. Security类
Security类管理已安装的提供程序和安全性属性。 它只包含静态方法,永远不会实例化。
提供了一系列API查询Provider、增加Provider、删除Provider
Provider[] arr = Security.getProviders();
3. SecureRandom随机生成器类
SecureRandom类是提供随机数生成器(RNG)功能的引擎类。 它不同于java.lang.Random类,因为它产生密码强的随机数。 如果生成器中的随机性不足,则会使保护机制变得更加容易。 在密码学中使用随机数字,例如生成加密密钥,算法参数等等。
4. MessageDigest摘要类
MessageDigest类是一个引擎类,用于提供密码安全的消息摘要(如SHA-256或SHA-512)的功能。 加密安全的消息摘要采用任意大小的输入(一个字节数组),并生成一个固定大小的输出,称为摘要或散列。
5. Signature 签名类
Signature类是一个引擎类,旨在提供加密数字签名算法(如DSA或RSAwithMD5)的功能。 密码安全签名算法采用任意大小的输入和私钥,并生成一个相对较短(通常是固定大小)的字节串,称为签名,具有以下属性:
- 只有私钥/公钥对的所有者才能创建签名。 任何拥有公钥的人都可以在计算上不可能恢复私钥
- 鉴于与用于生成签名的私钥相对应的公钥,应该有可能验证输入的真实性和完整性。
-
签名和公钥没有透露有关私钥的任何信息。
6. Cipher 加密解密类
Cipher类提供用于加密和解密的加密密码的功能。 加密是取数据(称为明文)和密钥的过程,并且产生对不知道密钥的第三方毫无意义的数据(密文)。 解密是相反的过程:取密文和密钥并产生明文。
7. SealedObject 密封对象类
8. Mac 消息认证码类
HMAC是基于密码散列函数的MAC。 HMAC可以与任何密码散列函数(例如SHA-256)结合秘密共享密钥一起使用。
9. Key 密钥接口
秘钥有两种表示形式:key(不透明)和keyspecs(透明)。
- 不透明的密钥接口无法直接访问密钥材料字段,只有定义的三种方法:getAlgorithm,getFormat和getEncoded。
- 透明的密钥,可以以通过相应规范类中定义的某个get方法单独访问每个关键字值。 例如,DSAPrivateKeySpec定义了getX,getP,getQ和getG方法来访问私钥x以及用于计算密钥的DSA算法参数:素数p,次数q和基数g。 如果密钥存储在硬件设备上,其规格可能包含有助于识别设备密钥的信息。
9.1 Key 不透明秘钥接口
秘钥可以通过以下方式获得:
- KeyGenerator (生成对称秘钥)
- KeyPairGenerator(生成非对称秘钥)
- Certificate证书
- KeyStore密码库
- 转换KeySpecs(使用KeyFactory)
java.security.Key接口是所有不透明密钥的顶层接口。
以下是在java.security.interfaces和javax.crypto.interfaces包中扩展Key接口的接口列表:
- SecretKey
- PBEKey
- PrivateKey
- DHPrivateKey
- DSAPrivateKey
- ECPrivateKey
- RSAMultiPrimePrivateCrtKey
- RSAPrivateCrtKey
- RSAPrivateKey
- PublicKey
- DHPublicKey
- DSAPublicKey
- ECPublicKey
- RSAPublicKey
SecretKey用于对称秘钥
PublicKey和PrivateKey接口分别用于非对称秘钥的公钥和私钥。
KeyPair类是密钥对(公钥和私钥)的简单持有者。
9.2 KeySpecs 透明秘钥接口
也叫密钥规范(密钥材料)接口
java.security.spec包中的密钥规范接口和类。
DESKeySpec是一种DES密钥规范。
X509EncodedKeySpec是一种公钥编码规范,它表示公钥的DER编码,根据X.509标准中规定的格式。
PKCS8EncodedKeySpec是一种私钥编码规范,以PKCS8标准中指定的格式对私钥进行DER编码。
KeySpecs实现的子类如下:
SecretKeySpec
-
EncodedKeySpec
- PKCS8EncodedKeySpec
- X509EncodedKeySpec
DESKeySpec
DESedeKeySpec
PBEKeySpec
DHPrivateKeySpec
DSAPrivateKeySpec
ECPrivateKeySpec
-
RSAPrivateKeySpec
- RSAMultiPrimePrivateCrtKeySpec
- RSAPrivateCrtKeySpec
DHPublicKeySpec
DSAPublicKeySpec
ECPublicKeySpec
RSAPublicKeySpec
10 生成器和工厂类的区别:
10.1 生成器 **Generator
生成器用于生成一个全新的对象(秘钥)。比如KeyGenerator生成SecretKey,KeyPairGenerator生成KeyPair。
生成器一般有两种方法来生成密钥对
- 以独立于算法的方式
void initialize(int keysize, SecureRandom random)
void initialize(int keysize)
- 以特定于算法的方式
void initialize(AlgorithmParameterSpec params, SecureRandom random)
void initialize(AlgorithmParameterSpec params)
KeyPairGenerator类,用于生成公钥和私钥对的引擎类。
KeyGenerator类, 用于为对称算法生成密钥。
10.2 工厂类 **Factory
工厂类用于将数据从一个现有的对象类型转换为另一个。一个密钥工厂可以用来在兼容的密钥规范之间进行转换。
比如SecretKeyFactory和KeyFactory,可以转换keySpecs为key。比如CertificateFactory,从字节输入流(FileInputStream)转换成证书(Certificate)对象。
秘钥工厂是双向的,密钥工厂用于将密钥(java.security.Key类型的不透明密钥)转换为密钥规范(以合适的格式对基础密钥材料进行透明表示),反之亦然。
比如KeyFactory类和SecretKeyFactory类:
11 KeyAgreement 秘钥协商类
Diffie-Hellman算法是一种密钥协议,是两方或多方可以建立相同密码密钥而不必交换任何秘密信息的协议。
生成共享密钥有如下几步:
- 创建KeyAgreement对象
KeyAgreement.getInstance()
- 初始化KeyAgreement
public void init(Key key);
public void init(Key key, SecureRandom random);
public void init(Key key, AlgorithmParameterSpec params);
public void init(Key key, AlgorithmParameterSpec params, SecureRandom random);
- 执行密钥协商阶段
public Key doPhase(Key key, boolean lastPhase);
- 生成共享密钥
public byte[] generateSecret();
public int generateSecret(byte[] sharedSecret, int offset);
public SecretKey generateSecret(String algorithm);
12. 密码库KeyStore
“密钥库”是一种可用于管理密钥和证书的存储库,一个密码库包含多个私钥和证书。
- KeyStore文件:用户密钥库默认存储在用户主目录中名为.keystore的文件中。
- 密码库工具:keytool和jarsigner的命令行工具,以及一个名为policytool的基于GUI的工具。
- 密钥库实现类型:
默认的keystore实现类型是“jks”,
推荐的keystore实现是“pkcs12”,
可以在java.security文件中的修改默认实现类型:
keystore.type=jks
KeyStore类
密钥库中有两种不同类型的条目:Key Entry(密钥条目)和Trusted Certificate Entry(可信任的证书条目)
KeyStore对象可以从文件中load()或store()到文件。并提供了一系列方法来操作key条目和Certificate条目。
13. AlgorithmParameters和AlgorithmParameterSpecs
AlgorithmParameters:不透明的加密参数接口
AlgorithmParameterSpecs:透明的加密参数接口
AlgorithmParameters或AlgorithmParameterSpecs用于配置算法的初始化参数
AlgorithmParameterGenerator生成器,是一个引擎类,用于生成一组适用于特定算法的全新参数,提供了生成AlgorithmParameters和AlgorithmParameterSpecs的方法。
14. CertificateFactory类
CertificateFactory类是定义证书工厂功能的引擎类,用于从其编码(文件流)生成证书和证书吊销列表(CRL)对象。
15. JCA如何在SSL / TLS实现中使用
- 编写Provider类的具体实现,并在Security类的提供者列表中注册
- 使用KeyManager(密钥库)和TrustManagerSSLContext(信任度)初始化SSLContext
- 使用上下文SSLContext,创建实际实现SSL / TLS协议的对象(SSLSocket和SSLEngine)。
- SSLSocket和SSLEngine将直接在通信对象中使用。
KeyManager和TrustManager作用:
- JSSE KeyManager负责向对等端显示使用的凭证(使用的密码标准、加密算法、证书、公钥、签名等)
- JSSE TrustManager负责验证从对等端收到的凭证,验证凭证有多种方式:其中之一是创建CertPath对象,并让JDK的内置公钥基础结构(PKI)框架处理验证。 在内部,CertPath实现可能会创建一个Signature对象,并使用它来验证证书链中的每个签名。
SSL/TLS握手过程:
客户端首先发送一个ClientHello消息给服务器。 服务器选择要使用的密码组,然后将其发回到ServerHello消息中,并根据套件选择开始创建JCA对象。 我们将在下面的例子中使用服务器唯一身份验证。
在第一个示例中,服务器尝试使用基于RSA的密码组,例如TLS_RSA_WITH_AES_128_CBC_SHA。 查询服务器的KeyManager,并返回相应的RSA条目。 服务器的凭证(即:证书/公钥)将在服务器的证书消息中发送。 客户端的TrustManager验证服务器的证书,如果接受,客户端使用SecureRandom对象生成一些随机字节。 然后使用已使用在服务器证书中找到的PublicKey初始化的加密非对称RSA密码对象对其进行加密。 此加密数据在客户端密钥交换消息中发送。 服务器将使用其相应的PrivateKey在解密模式下使用类似的密码恢复字节。 这些字节然后用于建立实际的加密密钥。
一旦建立了真实的加密密钥,秘密密钥就被用来初始化一个对称的密码对象,并且这个密码被用来保护所有传输中的数据。 为了帮助确定数据是否已被修改,创建MessageDigest并接收发往网络的数据的副本。 当数据包完成时,摘要(哈希)被附加到数据,并且整个数据包被密码加密。 如果使用诸如AES的分组密码,则必须填充数据以形成完整的块。 在另一端,这些步骤简单地颠倒过来。