java 安全體系主要分為
- JCA(Java Cryptography Architectrue)java加密體系
- JCE(Java Cryptography Extension)java加密拓展
- JSSE (Java Secure Socket Extesion)java套接字安全拓展
- JAAS(Java Authentication and Authentication Service)Java驗證和授權(quán)API
它們并不執(zhí)行各種算法,只是連接應(yīng)用和實際算法實現(xiàn)程序的一組接口。軟件開發(fā)商根據(jù)JCE接口,將各種算法實現(xiàn)后,打包成一個Provider,可以動態(tài)地加到Java運行環(huán)境中
Android Provider 當(dāng)中默認(rèn)包含的算法類型
// 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 安全體系擴(kuò)展性很強(qiáng),除了 jdk 提供的算法實現(xiàn)外 可以使用第三方j(luò)ava拓展,常用安全供應(yīng)者的有
- Bouncy Castle
- Commons Codec – Apache
如何配置添加第三方的安全供應(yīng)者
方法 1 .配置文件
Mac 桌面系統(tǒng)配置擴(kuò)展 provider ,并沒有加入 Bouncy Castle
配置文件在 jdk安裝目錄的 jre/lib/security/java.security

Android 系統(tǒng)配置在 security.properties

Android 默認(rèn)配置了 BouncyCastleProvider ,但是閹割版的,BouncyCastleProvider 類中有的,但是 Android 上不一定會有.

- 代碼動態(tài)添加
Security.addProvider(new BouncyCastleProvider());
(Android 手機(jī)可能不會生效,因為 Android 已經(jīng)默認(rèn)添加過閹割版本的 可以先移除系統(tǒng)自帶的再添加,不過部分手機(jī)仍然不會生效);
最穩(wěn)妥的辦法就是 當(dāng)使用到某種算法的時候就直接傳入一個 Provider 對象,就會使用到我們擴(kuò)展的安全供應(yīng)者的算法
Signature signature = Signature.getInstance("SM3withSM2", new BouncyCastleProvider());
Android 默認(rèn)配置的 Provider 初始化過程
在 java.security.Security 類當(dāng)中有一段靜態(tài)代碼塊去加載 security.properties 文件,就會把security.properties 的配置的屬性加載進(jìn)來到 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類中的靜態(tài)代碼塊中
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 獲取類名和參數(shù)構(gòu)建 ProviderConfig對象
/**
* Return a new ProviderList parsed from the java.security Properties.
*/
private ProviderList() {
List<ProviderConfig> 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 的類名 構(gòu)建 ProviderConfig對象 并且存儲在 ProviderConfig[] configs;數(shù)組當(dāng)中
/**
* 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);
}
調(diào)用 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;
}
調(diào)用各個 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;
}
調(diào)用 doLoadProvider 方法
private Provider doLoadProvider() {
return initProvider(className, Object.class.getClassLoader());
}
調(diào)用 initProvider 通過反射調(diào)用構(gòu)造函數(shù)獲取 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 類就會加載進(jìn)來并且創(chuàng)建對象.現(xiàn)在已將 BouncyCastleProvider 配置進(jìn)來了
舉例說明 MD5 算法是如何被加載進(jìn)來被使用的 ,其他的也類似.
BouncyCastleProvider 的構(gòu)造函數(shù) 調(diào)用父類的構(gòu)造函數(shù) PROVIDER_NAME ,這個是一個 key ,每個 Provider 通過這個參數(shù)也能夠找到相應(yīng)的 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
* <code>Security.addProvider()</code> mechanism.
*/
public BouncyCastleProvider()
{
super(PROVIDER_NAME, 1.56, info);
setup();
}
然后調(diào)用 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);
}
調(diào)用 loadAlgorithms ,通過 for 循環(huán)加載反射拿到相應(yīng)的類對象 如 MD5算法
就會加載 org.bouncycastle.jcajce.provider.digest.MD5$Mappings 這個類(org.bouncycastle.jcajce.provider.digest.MD5的靜態(tài)內(nèi)部類)
并且創(chuàng)建對象調(diào)用 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 方法調(diào)用 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調(diào)用了 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 方法調(diào)用 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 當(dāng)中
private transient Map<String,String> 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 參數(shù)分別為 ("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;
}
}
調(diào)用 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();
}
調(diào)用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;
}
調(diào)用 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;
}
調(diào)用 Provider 的ensureLegacyParsed 方法
在之前分析 BouncyCastleProvider 對象在初始化的過程中已經(jīng)把字符串
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 當(dāng)中 ,在 ensureLegacyParsed方法 for 循環(huán)取出調(diào)用 parseLegacyPut
private void ensureLegacyParsed() {
for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
parseLegacyPut(entry.getKey(), entry.getValue());
}
removeInvalidServices(legacyMap);
legacyChanged = false;
}
調(diào)用 Provider 的 parseLegacyPut ("MessageDigest.MD5","org.bouncycastle.jcajce.provider.digest.MD5$Digest");方法 解析 生成key 為 ServiceKey對象,Value 為 Provider.Service,放入 legacyMap當(dāng)中
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<ServiceKey,Service> 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);
}
調(diào)用 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"
然后調(diào)用 GetInstance 靜態(tài)內(nèi)部類GetInstance.Instance 的構(gòu)造函數(shù)(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 返回一個數(shù)組
最后
MessageDigest md = MessageDigest.getInstance("MD5",new BouncyCastleProvider());
中返回的就是 MessageDigest md = (MessageDigest)objs[0];
objs[0]就是 org.bouncycastle.jcajce.provider.digest.MD5$Digest 對象.以后使用這個對象執(zhí)行 MD5 的算法
這樣我們就通過添加 Provider 擴(kuò)展了 Java 體系的算法
經(jīng)過以上的分析,我們就可以自己自定義 Provider 擴(kuò)展的Java 的安全體系的算法
我們并不真的去實現(xiàn) MD5 算法,只是測試我們的 Provider 是否添加成功,自己寫的MD5算法是否被調(diào)用
自定義 Provider 擴(kuò)展的Java 的安全體系的算法
- 創(chuàng)建一個類繼承 Provider ,調(diào)用父類的構(gòu)造函數(shù)
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");
}
}
- 創(chuàng)建 com.example.MyMessageDigest5 類 這個類必須是 MessageDigestSpi的實現(xiàn)類
MessageDigest 繼承了 MessageDigestSpi ,最后 engineDigest()方法就是 md5 的計算結(jié)果
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();
}
}
最后的輸出結(jié)果,證明我們的 Provider 已添加,MD5算法 已被調(diào)用
