java 安全体系主要分为
- JCA(Java Cryptography Architectrue)java加密体系
- JCE(Java Cryptography Extension)java加密拓展
- JSSE (Java Secure Socket Extesion)java套接字安全拓展
- JAAS(Java Authentication and Authentication Service)Java验证和授权API
它们并不执行各种算法,只是连接应用和实际算法实现程序的一组接口。软件开发商根据JCE接口,将各种算法实现后,打包成一个Provider,可以动态地加到Java运行环境中
Android Provider 当中默认包含的算法类型
// JCA
addEngine("AlgorithmParameterGenerator", false, null);
addEngine("AlgorithmParameters", false, null);
addEngine("KeyFactory", false, null);
addEngine("KeyPairGenerator", false, null);
addEngine("KeyStore", false, null);
addEngine("MessageDigest", false, null);
addEngine("SecureRandom", false, null);
addEngine("Signature", true, null);
addEngine("CertificateFactory", false, null);
addEngine("CertPathBuilder", false, null);
addEngine("CertPathValidator", false, null);
addEngine("CertStore", false,
"java.security.cert.CertStoreParameters");
// JCE
addEngine("Cipher", true, null);
addEngine("ExemptionMechanism", false, null);
addEngine("Mac", true, null);
addEngine("KeyAgreement", true, null);
addEngine("KeyGenerator", false, null);
addEngine("SecretKeyFactory", false, null);
// JSSE
addEngine("KeyManagerFactory", false, null);
addEngine("SSLContext", false, null);
addEngine("TrustManagerFactory", false, null);
// JGSS
addEngine("GssApiMechanism", false, null);
// SASL
addEngine("SaslClientFactory", false, null);
addEngine("SaslServerFactory", false, null);
// POLICY
addEngine("Policy", false,
"java.security.Policy$Parameters");
// CONFIGURATION
addEngine("Configuration", false,
"javax.security.auth.login.Configuration$Parameters");
// XML DSig
addEngine("XMLSignatureFactory", false, null);
addEngine("KeyInfoFactory", false, null);
addEngine("TransformService", false, null);
// Smart Card I/O
addEngine("TerminalFactory", false,
"java.lang.Object");
Java 安全体系扩展性很强,除了 jdk 提供的算法实现外 可以使用第三方java拓展,常用安全供应者的有
- Bouncy Castle
- Commons Codec – Apache
如何配置添加第三方的安全供应者
方法 1 .配置文件
Mac 桌面系统配置扩展 provider ,并没有加入 Bouncy Castle
配置文件在 jdk安装目录的 jre/lib/security/java.security
Android 系统配置在 security.properties
Android 默认配置了 BouncyCastleProvider ,但是阉割版的,BouncyCastleProvider 类中有的,但是 Android 上不一定会有.
- 代码动态添加
Security.addProvider(new BouncyCastleProvider());
(Android 手机可能不会生效,因为 Android 已经默认添加过阉割版本的 可以先移除系统自带的再添加,不过部分手机仍然不会生效);
最稳妥的办法就是 当使用到某种算法的时候就直接传入一个 Provider 对象,就会使用到我们扩展的安全供应者的算法
Signature signature = Signature.getInstance("SM3withSM2", new BouncyCastleProvider());
Android 默认配置的 Provider 初始化过程
在 java.security.Security 类当中有一段静态代码块去加载 security.properties 文件,就会把security.properties 的配置的属性加载进来到 props 对象
static {
props = new Properties();
InputStream is = null;
InputStream propStream = Security.class.getResourceAsStream("security.properties");
is = new BufferedInputStream(propStream);
props.load(is);
}
在 java.security.Provider.Providers类中的静态代码块中
static {
// set providerList to empty list first in case initialization somehow
// triggers a getInstance() call (although that should not happen)
providerList = ProviderList.EMPTY;
// 1. 新建一个 ProviderList 对象
providerList = ProviderList.fromSecurityProperties();
// removeInvalid is specified to try initializing all configured providers
// and removing those that aren't instantiable. This has the side effect
// of eagerly initializing all providers.
final int numConfiguredProviders = providerList.size();
// 2.初始化并移除那些无法初始化的各个 Provider
providerList = providerList.removeInvalid();
if (numConfiguredProviders != providerList.size()) {
throw new AssertionError("Unable to configure default providers");
}
}
fromSecurityProperties 方法中会 new ProviderList();对象
在 sun.security.jca.ProviderList
读取 props 对象保存的security.properties
中security.provider 获取类名和参数构建 ProviderConfig对象
/**
* Return a new ProviderList parsed from the java.security Properties.
*/
private ProviderList() {
List configList = new ArrayList<>();
for (int i = 1; true; i++) {
String entry = Security.getProperty("security.provider." + i);
if (entry == null) {
break;
}
entry = entry.trim();
if (entry.length() == 0) {
System.err.println("invalid entry for " +
"security.provider." + i);
break;
}
int k = entry.indexOf(' ');
ProviderConfig config;
if (k == -1) {
config = new ProviderConfig(entry);
} else {
String className = entry.substring(0, k);
String argument = entry.substring(k + 1).trim();
config = new ProviderConfig(className, argument);
}
// Get rid of duplicate providers.
if (configList.contains(config) == false) {
configList.add(config);
}
}
}
这个就拿到了配置的 provider 的类名 构建 ProviderConfig对象 并且存储在 ProviderConfig[] configs;数组当中
/**
* Try to load all Providers and return the ProviderList. If one or
* more Providers could not be loaded, a new ProviderList with those
* entries removed is returned. Otherwise, the method returns this.
*/
ProviderList removeInvalid() {
int n = loadAll();
if (n == configs.length) {
return this;
}
ProviderConfig[] newConfigs = new ProviderConfig[n];
for (int i = 0, j = 0; i < configs.length; i++) {
ProviderConfig config = configs[i];
if (config.isLoaded()) {
newConfigs[j++] = config;
}
}
return new ProviderList(newConfigs, true);
}
调用 loadAll 方法
// attempt to load all Providers not already loaded
private int loadAll() {
int n = 0;
for (int i = 0; i < configs.length; i++) {
Provider p = configs[i].getProvider();
if (p != null) {
n++;
}
}
if (n == configs.length) {
allLoaded = true;
}
return n;
}
调用各个 ProviderConfig对象的 getProvider方法
/**
* Get the provider object. Loads the provider if it is not already loaded.
*/
synchronized Provider getProvider() {
// volatile variable load
Provider p = provider;
if (p != null) {
return p;
}
if (shouldLoad() == false) {
return null;
}
if (isLoading) {
return null;
}
try {
isLoading = true;
tries++;
p = doLoadProvider();
} finally {
isLoading = false;
}
provider = p;
return p;
}
调用 doLoadProvider 方法
private Provider doLoadProvider() {
return initProvider(className, Object.class.getClassLoader());
}
调用 initProvider 通过反射调用构造函数获取 Provider 对象
private Provider initProvider(String className, ClassLoader cl) throws Exception {
Class provClass;
if (cl != null) {
provClass = cl.loadClass(className);
} else {
provClass = Class.forName(className);
}
Object obj;
if (hasArgument() == false) {
obj = provClass.newInstance();
} else {
Constructor cons = provClass.getConstructor(CL_STRING);
obj = cons.newInstance(argument);
}
if (obj instanceof Provider) {
return (Provider)obj;
} else {
disableLoad();
return null;
}
}
然后 配置的 Provider 类就会加载进来并且创建对象.现在已将 BouncyCastleProvider 配置进来了
举例说明 MD5 算法是如何被加载进来被使用的 ,其他的也类似.
BouncyCastleProvider 的构造函数 调用父类的构造函数 PROVIDER_NAME ,这个是一个 key ,每个 Provider 通过这个参数也能够找到相应的 Provider 对象 .
public static final String PROVIDER_NAME = "BC";
/**
* Construct a new provider. This should only be required when
* using runtime registration of the provider using the
* Security.addProvider()
mechanism.
*/
public BouncyCastleProvider()
{
super(PROVIDER_NAME, 1.56, info);
setup();
}
然后调用 setup方法
/*
* Configurable digests
*/
private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest.";
private static final String[] DIGESTS =
{
// BEGIN android-removed
// "GOST3411", "Keccak", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224",
// "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool", "Blake2b"
// END android-removed
// BEGIN android-added
"MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512",
// END android-added
};
private void setup()
{
loadAlgorithms(DIGEST_PACKAGE, DIGESTS);
}
调用 loadAlgorithms ,通过 for 循环加载反射拿到相应的类对象 如 MD5算法
就会加载 org.bouncycastle.jcajce.provider.digest.MD5$Mappings 这个类(org.bouncycastle.jcajce.provider.digest.MD5的静态内部类)
并且创建对象调用 configure方法
private void loadAlgorithms(String packageName, String[] names)
{
for (int i = 0; i != names.length; i++)
{
Class clazz = null;
try
{
ClassLoader loader = this.getClass().getClassLoader();
if (loader != null)
{
clazz = loader.loadClass(packageName + names[i] + "$Mappings");
}
else
{
clazz = Class.forName(packageName + names[i] + "$Mappings");
}
}
catch (ClassNotFoundException e)
{
// ignore
}
if (clazz != null)
{
try
{
((AlgorithmProvider)clazz.newInstance()).configure(this);
}
catch (Exception e)
{ // this should never ever happen!!
throw new InternalError("cannot create instance of "
+ packageName + names[i] + "$Mappings : " + e);
}
}
}
}
在 org.bouncycastle.jcajce.provider.digest.MD5类中
package org.bouncycastle.jcajce.provider.digest;
public class MD5
{
private MD5()
{
}
/**
* MD5 HashMac
*/
public static class HashMac
extends BaseMac
{
public HashMac()
{
super(new HMac(new MD5Digest()));
}
}
public static class KeyGenerator
extends BaseKeyGenerator
{
public KeyGenerator()
{
super("HMACMD5", 128, new CipherKeyGenerator());
}
}
static public class Digest
extends BCMessageDigest
implements Cloneable
{
public Digest()
{
super(new MD5Digest());
}
public Object clone()
throws CloneNotSupportedException
{
Digest d = (Digest)super.clone();
d.digest = new MD5Digest((MD5Digest)digest);
return d;
}
}
public static class Mappings
extends DigestAlgorithmProvider
{
private static final String PREFIX = MD5.class.getName();
public Mappings()
{
}
public void configure(ConfigurableProvider provider)
{
provider.addAlgorithm("MessageDigest.MD5", PREFIX + "$Digest");
provider.addAlgorithm("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md5, "MD5");
addHMACAlgorithm(provider, "MD5", PREFIX + "$HashMac", PREFIX + "$KeyGenerator");
addHMACAlias(provider, "MD5", IANAObjectIdentifiers.hmacMD5);
}
}
}
configure 方法调用 BouncyCastleProvider的 addAlgorithm
provider.addAlgorithm("MessageDigest.MD5", PREFIX + "$Digest");
provider.addAlgorithm("Alg.Alias.MessageDigest." + PKCSObjectIdentifiers.md5, "MD5");
PKCSObjectIdentifiers.md5 == "1.2.840.113549.2.5"
BouncyCastleProvider的 addAlgorithm调用了 put方法 ,put 是父类 Provider的方法
public void addAlgorithm(String key, String value)
{
if (containsKey(key))
{
throw new IllegalStateException("duplicate provider key (" + key + ") found");
}
put(key, value);
}
Provider的put 方法调用 implPut
@Override
public synchronized Object put(Object key, Object value) {
check("putProviderProperty."+name);
return implPut(key, value);
}
implPut 就会把
字符串 key "MessageDigest.MD5",和 value 类名 org.bouncycastle.jcajce.provider.digest.MD5$Digest
key "Alg.Alias.MessageDigest.1.2.840.113549.2.5" 和 value "MD5"
存入legacyStrings Map 当中
private transient Map legacyStrings;
private Object implPut(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.put((String)key, (String)value);
}
return super.put(key, value);
}
如何使用 BouncyCastleProvider 的 MD5 方法
MessageDigest md = MessageDigest.getInstance("MD5",new BouncyCastleProvider());
MessageDigest .getInstance 参数分别为 ("MD5",BouncyCastleProvider对象)
public static MessageDigest getInstance(String algorithm,
Provider provider)
throws NoSuchAlgorithmException
{
if (provider == null)
throw new IllegalArgumentException("missing provider");
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
if (objs[0] instanceof MessageDigest) {
MessageDigest md = (MessageDigest)objs[0];
md.provider = (Provider)objs[1];
return md;
} else {
MessageDigest delegate =
new Delegate((MessageDigestSpi)objs[0], algorithm);
delegate.provider = (Provider)objs[1];
return delegate;
}
}
调用 Security 的 getImpl ("MD5","MessageDigest",BouncyCastleProvider对象)
static Object[] getImpl(String algorithm, String type, Provider provider)
throws NoSuchAlgorithmException {
return GetInstance.getInstance
(type, getSpiClass(type), algorithm, provider).toArray();
}
调用GetInstance类的 getSpiClass 方法
加载 java.security.MessageDigestSpi 类并把它加入到 spiMap 缓存起来方便下次使用
private static Class getSpiClass(String type) {
Class clazz = spiMap.get(type);
if (clazz != null) {
return clazz;
}
try {
clazz = Class.forName("java.security." + type + "Spi");
spiMap.put(type, clazz);
return clazz;
} catch (ClassNotFoundException e) {
throw new AssertionError("Spi class not found", e);
}
}
GetInstance类 getInstance ("MessageDigest",MessageDigestSpi.class,"MD5",BouncyCastleProvider对象)
public static Instance getInstance(String type, Class clazz,
String algorithm, Provider provider)
throws NoSuchAlgorithmException {
return getInstance(getService(type, algorithm, provider), clazz);
}
GetInstance类 getService ("MessageDigest","MD5",BouncyCastleProvider对象)
public static Service getService(String type, String algorithm,
Provider provider) throws NoSuchAlgorithmException {
Service s = provider.getService(type, algorithm);
return s;
}
调用 BouncyCastleProvider 父类的Provider getService ("MessageDigest","MD5")
public synchronized Service getService(String type, String algorithm) {
ServiceKey key = previousKey;
if (key.matches(type, algorithm) == false) {
key = new ServiceKey(type, algorithm, false);
previousKey = key;
}
ensureLegacyParsed();
return (legacyMap != null) ? legacyMap.get(key) : null;
}
调用 Provider 的ensureLegacyParsed 方法
在之前分析 BouncyCastleProvider 对象在初始化的过程中已经把字符串
key "MessageDigest.MD5",和 value 类名 org.bouncycastle.jcajce.provider.digest.MD5$Digest
key "Alg.Alias.MessageDigest.1.2.840.113549.2.5" 和 value "MD5"
存入legacyStrings 的Map 当中 ,在 ensureLegacyParsed方法 for 循环取出调用 parseLegacyPut
private void ensureLegacyParsed() {
for (Map.Entry entry : legacyStrings.entrySet()) {
parseLegacyPut(entry.getKey(), entry.getValue());
}
removeInvalidServices(legacyMap);
legacyChanged = false;
}
调用 Provider 的 parseLegacyPut ("MessageDigest.MD5","org.bouncycastle.jcajce.provider.digest.MD5$Digest");方法 解析 生成key 为 ServiceKey对象,Value 为 Provider.Service,放入 legacyMap当中
ServiceKey
.type ="MessageDigest"
.originalAlgorithm = "MD5"
.algorithm = "MD5"
Provider.Service
.provider 属性为BouncyCastleProvider对象
.type = "MessageDigest";
.algorithm = "MD5"
.className = "org.bouncycastle.jcajce.provider.digest.MD5$Digest"
并为 Provider.Service 添加 aliases; 1.2.840.113549.2.5,
private transient Map legacyMap;
private final static String ALIAS_PREFIX = "Alg.Alias.";
private final static String ALIAS_PREFIX_LOWER = "alg.alias.";
private final static int ALIAS_LENGTH = ALIAS_PREFIX.length();
private void parseLegacyPut(String name, String value) {
if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
// e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
// aliasKey ~ MessageDigest.SHA
String stdAlg = value;
String aliasKey = name.substring(ALIAS_LENGTH);
String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
if (typeAndAlg == null) {
return;
}
String type = getEngineName(typeAndAlg[0]);
String aliasAlg = typeAndAlg[1].intern();
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
legacyMap.put(key, s);
}
legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
s.addAlias(aliasAlg);
} else {
String[] typeAndAlg = getTypeAndAlgorithm(name);
if (typeAndAlg == null) {
return;
}
int i = typeAndAlg[1].indexOf(' ');
if (i == -1) {
// e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
String type = getEngineName(typeAndAlg[0]);
String stdAlg = typeAndAlg[1].intern();
String className = value;
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
legacyMap.put(key, s);
}
s.className = className;
} else { // attribute
// e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
String attributeValue = value;
String type = getEngineName(typeAndAlg[0]);
String attributeString = typeAndAlg[1];
String stdAlg = attributeString.substring(0, i).intern();
String attributeName = attributeString.substring(i + 1);
// kill additional spaces
while (attributeName.startsWith(" ")) {
attributeName = attributeName.substring(1);
}
attributeName = attributeName.intern();
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
legacyMap.put(key, s);
}
s.addAttribute(attributeName, attributeValue);
}
}
}
GetInstance类 getInstance (Provider.Service 对象,MessageDigestSpi.class)
public static Instance getInstance(Service s, Class clazz)
throws NoSuchAlgorithmException {
Object instance = s.newInstance(null);
checkSuperClass(s, instance.getClass(), clazz);
return new Instance(s.getProvider(), instance);
}
调用 Provider.Service newInstance方法,会加载 org.bouncycastle.jcajce.provider.digest.MD5$Digest 类并反射生成对象赋值给 instance
Provider.Service
.provider 属性为BouncyCastleProvider对象
.type = "MessageDigest";
.algorithm = "MD5"
.className = "org.bouncycastle.jcajce.provider.digest.MD5$Digest"
然后调用 GetInstance 静态内部类GetInstance.Instance 的构造函数(BouncyCastleProvider对象,instance)
/**
* Static inner class representing a newly created instance.
*/
public static final class Instance {
// public final fields, access directly without accessors
public final Provider provider;
public final Object impl;
private Instance(Provider provider, Object impl) {
this.provider = provider;
this.impl = impl;
}
// Return Provider and implementation as an array as used in the
// old Security.getImpl() methods.
public Object[] toArray() {
return new Object[] {impl, provider};
}
}
返回的 toArray 返回一个数组
最后
MessageDigest md = MessageDigest.getInstance("MD5",new BouncyCastleProvider());
中返回的就是 MessageDigest md = (MessageDigest)objs[0];
objs[0]就是 org.bouncycastle.jcajce.provider.digest.MD5$Digest 对象.以后使用这个对象执行 MD5 的算法
这样我们就通过添加 Provider 扩展了 Java 体系的算法
经过以上的分析,我们就可以自己自定义 Provider 扩展的Java 的安全体系的算法
我们并不真的去实现 MD5 算法,只是测试我们的 Provider 是否添加成功,自己写的MD5算法是否被调用
自定义 Provider 扩展的Java 的安全体系的算法
- 创建一个类继承 Provider ,调用父类的构造函数
public class MyProvider extends Provider {
/**
* Constructs a provider with the specified name, version number,
* and information.
*
* @param name the provider name.
* @param version the provider version number.
* @param info a description of the provider and its services.
*/
protected MyProvider(String name, double version, String info) {
super(name, version, info);
}
public static final String PROVIDER_NAME = "com.example.MyProvider";
MyProvider(){
this(PROVIDER_NAME,1.4,"com.example.MyProvider info_version v1.4");
init();
}
private void init() {
put("MessageDigest.MD5","com.example.MyMessageDigest5");
}
}
- 创建 com.example.MyMessageDigest5 类 这个类必须是 MessageDigestSpi的实现类
MessageDigest 继承了 MessageDigestSpi ,最后 engineDigest()方法就是 md5 的计算结果
public class MyMessageDigest5 extends MessageDigest {
public MyMessageDigest5() {
super("MD5");
}
protected void engineUpdate(byte input) {
}
protected void engineUpdate(byte[] input, int offset, int len) {
}
protected byte[] engineDigest() {
return "MyMessageDigest5 hello".getBytes();
}
protected void engineReset() {
}
}
- 使用 MyProvider ,有两种使用方式一种是直接传入Provider对象,一种是用 Security.addProvider 接口添加再用 Provider的名字访问
private static String src = "hello provider";
public static void main(String argv[]) {
try {
MessageDigest md = MessageDigest.getInstance("MD5", new MyProvider());
byte[] md5Bytes = md.digest(src.getBytes());
System.out.println(new String(md5Bytes));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
Security.addProvider(new MyProvider());
MessageDigest md = MessageDigest.getInstance("MD5", MyProvider.PROVIDER_NAME);
byte[] md5Bytes = md.digest(src.getBytes());
System.out.println(new String(md5Bytes));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
}
最后的输出结果,证明我们的 Provider 已添加,MD5算法 已被调用