Android的加密缓存(上)--JCA基础

在Java的知识体系中,Java平台安全是很重要的一部分。作为Android开发者,其实这部分知识对我们来说既陌生又熟悉。说熟悉,Android中Apk打包离不开的jks签名文件,Android 6.0提供的指纹识别接口的调用(Demo), 都是建立在Android平台安全的体系上。而Android平台安全又是建立在Java平台安全的这个坚实的基础上。说陌生,对于Java密码学架构(Java Cryptography Architecture 简称JCA)的还挺陌生,该系列文章分为上下两部分,这一篇主要聊聊JCA的基础知识,可能有些枯燥,在下篇,我会结合具体代码,完成一次简单的字符串缓存加密和解密。

JCA的基本介绍

先看来自《Java Cryptography Architecture
(JCA) Reference Guide
》的一段背景介绍:

Java平台重视安全性,包括语言安全性,密码学,公钥基础设施,身份验证,安全通行和访问控制。
JCA是JDk平台上非常重要的部分,通过包含一个 “provider”架构,设计了一套API为包括 数字签名、消息摘要、证书和证书验证、加密(对称和非对称,分组和流式密码)、秘钥生成和管理、安全随机码生成等功能服务。

可以看出,JCA是Java平台安全的基础,其它相关的还有Java密码学拓展(Java Cryptography Extension 简称JCE)、Java Secure Socket Extension (JSSE) 、Java Generic Security Services (JGSS)。

java密码学架构

JCA的设计原则

JCA在设计之初就遵循一些基本原则,也就是

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

应用无需实现具体的安全算法,或者说应用请求来自Java平台的安全服务。这些安全服务在“providers”(下面会介绍)中实现,这些providers可以互相协作,与应用之间没有必然的绑定限制。Java平台提供了现成的实现算法的providers,应用也可以通过自定义providers来实现特殊的算法。

JCA的架构

Cryptographic Service Providers CSP 密码服务提供者

刚才在设计原则那部分,反复提到了“providers”,那providers到底是个什么东西呢? JCA文档是这么介绍的:

a package or set of packages that supply a concrete implementation of a subset of the JDK Security API cryptography features
一个包或包结合,提供JDK安全API密码特性的具体实现

这么看还是很抽象,首先要了解java.security.Provider,这个抽象类是所有安全提供者的基础类。每一个CSP,也就是我们的密码服务提供者,都包含java.security.Provider类的一个实例,这个实例包含了“providers”的名称和所有实现的安全服务/算法的列表,当我们需要某种算法时,JCA框架就会去查询这个“providers”数据库,找到匹配的实例并创建它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Provider extends Properties {

private String name;

private String info;

private double version;

private transient Set<Map.Entry<Object,Object>> entrySet = null;
private transient int entrySetCallCount = 0;

private transient boolean initialized;

...
}

引用下JCA文档中的这张图:

java密码学架构--02

配合这张图就很清晰了,我们开发者处于应用层,JCA框架层对应用层只提供接口,具体的实现逻辑被封装起来。

1
2
//我们仅需通过具体的Engine class并提供需要的算法名称“MD5”,我们就可以获得一个provider实现的消息摘要实例
MessageDigest md = MessageDigest.getInstance("MD5");

Key Management 秘钥的管理

KeyStore是一个数据库,它可以被用于管理秘钥和证书的仓库。秘钥可用于应用的的数据验证,加密或签名。Sun Microsystems提供了默认的KeyStore实现。它将秘钥实现为一个文件,使用专有的秘钥类型(格式)叫“jks”.其他的秘钥格式如”jceks”(一种比jks加密强度更大的秘钥库格式)、”pkcs12”等。Android开发者应该很熟悉jks,打包生成apk的时候,我们就会用到jkd格式的秘钥。

例如,Android系统提供了默认的KeyStore,可以通过名称“AndroidKeyStore”来获取

1
2

KeyStore mKeyStore = KeyStore.getInstance("AndroidKeyStore");

Engine Classes and Algorithms 引擎类和算法

例如上文MessageDigest就是一类引擎类,1.4会介绍一些常用的引擎类,这里就不多做介绍了。

JCA的核心类和接口

看到这,想必大家对Providers,KeyStore,引擎类, 算法这些概念有了一些基础的认知。先放松一下,看一下gakki的笑容,放松一下。

gakki

骚年,我们继续。从API这个角度入手,了解一下JCA框架有哪些基础的类。了解之后,才方便我们后面的实战部分。

JCA的类大致可以分为以下几种:

  • Provider and Security 类
  • 各种Engine类,如SecureRandom, MessageDigest, Signature, Cipher, Mac, KeyFactory, SecretKeyFactory, KeyPairGenerator, KeyGenerator, KeyAgreement, AlgorithmParameters, AlgorithmParameterGenerator , KeyStore, and CertificateFactory
  • Key 接口和类
  • 算法参数规格接口和类(the Algorithm Parameter Specification Interfaces and Classes)
    秘钥规格接口和类(the Key Specification Interfaces and Classes)
  • 多种支持和便利接口和类

Key

首先了解一下秘钥,对应的定义是java.security.Key,是所有opaque keys的顶层接口,所谓opaque keys,也就是无法直接访问到构成key的材料,也就是说opaque keys给予了访问限制。只能通过getAlgorithm()、getFormat()、getEncoded()这些方法来获取到秘钥指定的信息。

1
2
3
4
5
6
7
8
9
10
public interface Key extends java.io.Serializable {

static final long serialVersionUID = 6603384152749567654L;

public String getAlgorithm();

public String getFormat();

public byte[] getEncoded();
}

KeyStore

用于创建和管理秘钥库, KeyStore是秘钥的数据库。秘钥库中的私有秘钥有与之相关的证书链,用于验证相关联的公有密钥。秘钥库也包含来自受信任实体的证书。上文有介绍,这里就不多说了。

KeyGenerator

通过使用特殊算法来生成秘钥,如果希望生成非对称秘钥,请使用KeyPairGenerator

初始化:有两种初始化方式:

KeyGenerator

  • 算法无关
1
2
3
4
5
public void init(SecureRandom random);

public void init(int keysize);

public void init(int keysize, SecureRandom random);
  • 算法相关
1
2
3
public void init(AlgorithmParameterSpec params);

public void init(AlgorithmParameterSpec params, SecureRandom random);

生成秘钥

1
public SecretKey generateKey();
  • AlgorithmParameterSpec

    算法参数,看源码可知只是个接口,具体的实现如KeyGenParameterSpec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package java.security.spec;

/**
* A (transparent) specification of cryptographic parameters.
*
* <P> This interface contains no methods or constants. Its only purpose
* is to group (and provide type safety for) all parameter specifications.
* All parameter specifications must implement this interface.
*
* @author Jan Luehe
*
*
* @see java.security.AlgorithmParameters
* @see DSAParameterSpec
*
* @since 1.2
*/

public interface AlgorithmParameterSpec { }

KeyGenParameterSpec

参数很多,显然也是通过构建者模式进行的初始化,构建者AlgorithmParameterSpec.Builder,重要的参数有
秘钥别名,秘钥目的,block modes、EncryptionPadding等

PurposeEnum

秘钥目的:加密、解密、签名、验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public @interface PurposeEnum {}

/**
* Purpose of key: encryption.
*/
public static final int PURPOSE_ENCRYPT = 1 << 0;

/**
* Purpose of key: decryption.
*/
public static final int PURPOSE_DECRYPT = 1 << 1;

/**
* Purpose of key: signing or generating a Message Authentication Code (MAC).
*/
public static final int PURPOSE_SIGN = 1 << 2;

/**
* Purpose of key: signature or Message Authentication Code (MAC) verification.
*/
public static final int PURPOSE_VERIFY = 1 << 3;

下面介绍几个Engine class

Cipher

用于数据的加密和解密,通过秘钥初始化。有几种不同的算法:如 对称加密协议 symmetric bulk encryption(AES, DES, DESede, Blowfish, IDEA),流式加密 stream encryption(RC4等), 非对称加密 asymmetric encryption(RSA等), 密码基础的加密 password-based encryption(PBE)

signature

提供用于密码数字签名算法(如DSA或RSAwithMD5)的功能

MessageDigest

提供密码信息摘要(如SHA-1或MD5)的功能,接受任意直接长度的输入(byte[]格式),生成
固定长度的输出(称为digest或hash)

Mac

Message Authentication Code的缩写,与MessageDigest类似, 用于检测信息的完整性以及在不可靠媒介上存储(包含一个secret key,持有正确key的人能够验证已接收的信息)


** 参考资料 **