Java Cryptography Architecture(JCA)——加密的那点儿事儿(加密服务提供者、算法名称、转换名称)

来源:互联网 发布:淘宝网玉手镯 编辑:程序博客网 时间:2024/05/25 16:37

在我们对数据进行安全加密和使用算法功能的时候,往往会碰到所谓的:“加密服务提供者”——即:Provider,“所请求算法的名称”——即:algorithm,“转换名称”——即:transformation(如:AES/ECB/PKCS7Padding)等这样的字眼,很多同学都对这模棱两可,也懒得看官网上的源码,今天我就和大家分享一下ORACLE官网上是怎么说的。

这里给出几个API,和oracle官网的JAC文档,想更详细了解的可以自己看看,如果发现本人理解有偏差,请留言,多谢!

Provider —— http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/security/Provider.html

Security —— http://jszx-jxpt.cuit.edu.cn/JavaAPI/java/security/Security.html

JAC —— http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html

建议用Google浏览器看,Google自带翻译功能,有助于阅读。

一、Java的加密体系结构——JCA

首先说一下JCA(Java Cryptography Architecture),叫Java加密体系结构,是Java平台的重要组成部分。Java平台强调安全性,包括语言安全、密码学、公钥基础设施、认证、安全通信和访问控制等,而JCA可以轻松地将安全功能集成到我们的应用程序中。在JDK 1.4之前,JCE是一个非捆绑产品,因此,JCA和JCE经常被称为单独的、独特的组件。由于JCE现在已经被JDK捆绑在一起,这个区别变得越来越不明显。由于JCE使用与JCA相同的架构,所以JCE应该更适当地被认为是JCA的一部分。

(1)JCA围绕这些原则设计:

实施独立性和互操作性
算法独立性和可扩展性
实现独立性和算法独立性是互补的;您可以使用加密服务,如数字签名和消息摘要,而不必担心实现细节,甚至是形成这些概念基础的算法。尽管无法完成算法独立性,但JCA提供了标准化的,特定于算法的API。当不实现独立性时,JCA允许开发人员指定具体的实现。
通过定义加密“引擎”(服务)的类型以及定义提供这些加密引擎的功能的类来实现算法独立性。这些类被称为引擎类,比如有: MessageDigest,Signature,KeyFactory,KeyPairGenerator,和Cipher等类。
使用基于“提供者”的体系结构实现独立性。术语加密服务提供商(CSP)(与本文档中的“提供者”互换使用)是指实现一个或多个加密服务的包或一组包,例如数字签名算法,消息摘要算法和密钥转换服务。程序可以简单地请求Signature实现特定服务(例如DSA签名算法)的特定类型的对象(如:Signature对象),并从安装的提供者之一获得实现。如果需要,程序可以替代地从特定提供商请求实现。提供商可能会透明地更新应用程序,例如,当有更快或更安全的版本可用时。
实现互操作性意味着各种实现可以彼此工作,使用对方的密钥或验证彼此的签名。这意味着例如对于相同的算法,由一个提供商产生的密钥将被另一个提供者使用,并且由一个提供商生成的签名将被另一个提供商验证。
算法可扩展性意味着可以轻松添加适合其中一个受支持引擎类的新算法。

(2)JDK中的JCA包括两个软件组件:

1、该框架定义和支持供应商提供实施的加密服务。该框架包括的包有java.security、javax.crypto、javax.crypto.spec和 javax.crypto.interfaces。这些包为Java中的安全框架、cryptographic(加密)操作、密钥规范和算法参数规范提供类和接口。

2、实际的加密服务供应商(即:Provider),如Sun, SunRsaSign,SunJCE等,其中包含实际的加密的实现。

每当提到特定的JCA提供者时(即:Provider),将通过提供者的名称来明确地引用该提供者。

(3)如何使用JCA

要使用JCA,应用程序只需要一个特定类型的对象(例如:MessageDigest)和特定的算法或服务(例如:“MD5”算法),并从一个已安装的提供程序获取实现,或者,程序可以从特定提供者请求对象。每个提供者都有一个名称用于引用它。

md = MessageDigest.getInstance(“MD5”); 

md = MessageDigest.getInstance(“MD5”,“ProviderC”);

下图说明了请求“MD5”消息摘要实现。该图显示了实现各种消息摘要算法(“SHA-1”,“MD5”,“SHA-256”和“SHA-512”)的三个不同提供者。提供者从左到右按优先顺序排列(1-3)。在第一个图示中,应用程序请求MD5算法实现而不指定提供者名称。以优先顺序搜索提供者,并返回提供该特定算法ProviderB的第一个提供者的实现。在第二个图中,应用程序从特定提供者 ProviderC 请求MD5算法实现。这次从ProviderC的实现被返回。


在Sun JDK加密实现经由若干不同的供应商分布(Sun, SunJSSE,SunJCE,SunRsaSign)主要由于历史原因,但通过的功能以及他们提供的算法的类型程度较轻。其他Java运行时环境可能不一定包含这些Sun提供程序,因此除非知道特定提供程序可用,否则应用程序不应请求提供者特定的实现。

 

二、安全API——Java的核心API

安全API(The Security API),它是Java编程语言的核心API,包含在java.security包(及其子包)中。该API的目的是允许开发人员将不管是低级别还是高级别的安全功能并入他们的程序中。Security是包java.security下的一个类(包的地址为:%JAVA_HOME%\jre\lib\security\java.security),此类集中了所有的安全属性和常见的安全方法,其主要用途之一是管理提供程序。它只包含静态方法,从不实例化,添加或删除提供者以及设置Security属性的方法只能由受信任的程序执行。

目前,“可信程序”包括:

1、一个本地应用程序不在安全管理器下运行

2、具有许可执行指定方法的小程序或应用程序。

可信的的程序要执行一个操作(例如添加提供程序),需要该小程序被授予该特定操作的适当权限。JDK安装的策略配置文件从指定代码源的代码能得到一些权限(系统资源的访问类型)。(有关详细信息,请参阅下文以及 “默认策略实施和策略文件语法”和“Java安全体系结构规范”文件。)

       既然Security类主要用途之一是管理提供者,那就来总结了 Security类中可用于查询哪些 Provider安装的方法,以及在运行时安装或删除提供程序的方法。

1、获得Provider(提供者)

static Provider[] getProviders();返回一个包含所有已安装提供程序的数组(从技术上讲,Provider每个程序包提供程序的子类)。Providers在数组中的顺序是它们的优先顺序。

static Provider getProvider(String providerName);返回以providerName命名的Provider。如果 Provider没有找到,则返回null。

2、添加Provider(提供者)

static int addProvider(Provider provider);在已安装的Providers的列表的末尾添加provider,并返回其添加的位置坐标,如果它已被安装,则返回-1。

static int insertProviderAt(Provider provider, int position);在指定位置position处添加提供者provider,并且将position位置及大于position位置的所有Provider都向上移动一个位置(朝向已安装提供程序列表的尾部)。此方法返回添加的provider位置坐标,如果它已被安装,则返回-1。

 3、删除Provider(提供者)

static void removeProvider(String name);删除指定名字为name的Provider。当指定的提供者被删除时,位于大于指定Provider位置的所有Provider都向下移动一个位置(朝向已安装提供程序列表的头部)。

注意:如果要更改提供者的位置,则必须先删除它,然后将其插入新的位置。

             

三、提供者Provider

Provider是java.security包下的一个类。此类表示 Java 安全 API "provider",这里 provider 实现了 Java 安全性的一部分或者全部。“Cryptographic Service Provider”(即:加密服务提供者,与本文的Provider可以互换使用)是指提供JDK Security API加密功能子集的具体实现的软件包或一组软件包,而Provider类正是这样的包或包集的接口。java.security.Provider是所有安全提供商的基类,它具有访问提供商名称,版本号和其他信息的方法。请注意,除了注册加密服务的实现之外, Provider类还可以用于注册可能被定义为JDK Security API或其扩展之一的其他安全服务的实现。

(1)provider 可以实现的服务包括:

1、算法(如 DSA、RSA、MD5 或 SHA-1)。

2、密钥的生成、转换和管理设施(如用于特定于算法的密钥)。

每个 provider 有一个名称和一个版本号,并且在每个它装入运行时中进行配置。

有关特定类型的provider、加密服务 provider 如何工作和安装的信息,请参阅 "Java Cryptography Architecture API Specification &Reference" 中的 The Provider Class。但是,请注意 provider 能够被用来实现 Java 中的任何安全服务,这些安全服务使用带有适合下层的实现选择的可插入架构。

(2)请求和提供提供者的几种方式

对于API中的每个引擎类,通过调用引擎类中的一个方法getInstance来请求和实例化,可以随意指定所需算法的名称,期望的提供者的名称(或类)

static EngineClassNamegetInstance(String algorithm) throws NoSuchAlgorithmException

static EngineClassNamegetInstance(String algorithm,String provider) throwsNoSuchAlgorithmException,NoSuchProviderException

static EngineClassNamegetInstance(String algorithm,Provider provider) throwsNoSuchAlgorithmException

其中EngineClassName是所需的引擎类型(MessageDigest /Cipher / etc)。例如:

MessageDigest md =MessageDigest.getInstance(“MD5”);

KeyAgreement ka = KeyAgreement.getInstance(“DH”,“SunJCE”);

分别返回“MD5”下的MessageDigest和“DH”下的KeyAgreement对象的实例。

注意:算法名称不区分大小写。例如,以下所有调用都是等价的:

MessageDigest.getInstance(“SHA-1”)

MessageDigest.getInstance(“sha-1”)

MessageDigest.getInstance(“sHa-1”)

一些提供者也可以选择指代相同算法的别名。例如,“SHA-1”算法可能被称为“SHA1”。但是,应用程序应使用标准名称而不是别名,因为并非所有提供者可能以相同的方式将算法名称进行别名。

如果没有指定提供者,则getInstance会搜索已注册的提供者实现与命名算法相关联的所请求的加密服务。在任何给定的Java虚拟机(JVM)中,提供者按照给定的优先顺序进行安装,如果没有请求特定的提供者,则会按提供者列表的顺序搜索。例如:假设有安装在JVM两家供应商PROVIDER_1和PROVIDER_2

PROVIDER_1实现SHA1withDSA,SHA-1,MD5,DES和DES3。

PROVIDER_1具有优先级1(最高优先级)。

PROVIDER_2实现SHA1withDSA,MD5,RSA,MD2,RSA,MD2,MD5,RC4,RC5,DES和RSA。

PROVIDER_2有优先级2。

现在来看看三种情况:

1.如果我们正在寻找一个MD5实现。两个提供商都提供这样的实现。在PROVIDER_1 返回的实现,因为PROVIDER_1具有最高优先级,并首先搜索。

2.如果我们正在寻找一个MD5withRSA签名算法, PROVIDER_1首先要搜索它。没有找到实现,所以PROVIDER_2被搜索。由于找到了一个实现,它被返回。

3.假设我们正在寻找一个SHA1withRSA签名算法。由于没有安装的提供程序实现它,所以抛出一个NoSuchAlgorithmException。

提供者中的getInstance方法是为了给那些想指定算法提供者的开发人员的。例如,联邦机构将希望使用已获得联邦认证的提供商实施。我们假设SHA1withDSA实现从PROVIDER_1没有收到这样的认证,而DSA的实现PROVIDER_2已经收到了。

然后联邦机构计划将进行以下呼吁,具体说明PROVIDER_2该计划具有认证实施:

签名dsa = Signature.getInstance(“SHA1withDSA”,“PROVIDER_2”);

在这种情况下,如果PROVIDER_2未安装,即使另一个已安装的提供程序实现了请求的算法,还是会抛出一个NoSuchProviderException。

程序还可以选择获取所有已安装的提供程序的列表(使用Security类中的getProviders方法)并从列表中选择一个。

注意:通用应用程序不应要求特定提供商提供加密服务。否则,应用程序与其他Java实现可能不可用的特定提供程序相关联。他们还可能无法利用具有比特定请求的提供商更高的优先级顺序的可用的优化提供者(例如通过PKCS11的硬件加速器或诸如Microsoft的MSCAPI的本地操作系统实现)。

每个Provider类实例都有一个(当前是区分大小写的)名称,一个版本号和一个提供者及其服务的字符串描述。你可以通过调用Provider的以下方法来查询实例的信息:

public String getName()

public double getVersion()

public String getInfo()


四、实例

根据上面的讲解,我们现在开始动手得到我们想要的。

(1)得到Security中包含的提供者

在main方法中写入以下代码,就可打印出Security中所有的提供者(包括每个提供者的名字、版本、详情)

Provider[] arr = Security.getProviders();for (int i = 0; i < arr.length; i++) {System.out.println("名字:" + arr[i].getName() + "\n版本:" + arr[i].getVersion() + "\n详情:" + arr[i].getInfo() + "\n");}

其打印结果样式为(因提供者比较多,就不一一列举,想看的同鞋,可以自己打印看看):

名字:SUN
版本:1.8
详情:SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS & DKS keystores; PKIX CertPathValidator; PKIX CertPathBuilder; LDAP, Collection CertStores, JavaPolicy Policy; JavaLoginConfig Configuration)

(2)得到提供者中的所有属性名字及其对应的值

在(1)中我们可以得到存储提供者的集合Provider[],根据Provider的API我们可看到其中有一个方法entrySet(),其返回值为Set<Map.Entry<Object,Object>>,调用该方法后返回此 Provider 中所包含的属性项,说明Provider的属性是以键值对的方式存储。这样我们就可以打印处其中的属性并找到算法了。在main方法中写如下代码:
Provider[] arr = Security.getProviders();for (int i = 0; i < arr.length; i++) {Set keys = arr[i].keySet();            for (Iterator it=keys.iterator(); it.hasNext(); ) {                String key = (String)it.next();                // 每个keys的属性项可能由一个字符串组成,或是有一个空格分隔的字符串组成,带空格的字符串有两种情况(命名:空格前为属性项,空格后为属性名)                // 1、相同属性项下的不同属性名(如:“Provider.id name” 和 “Provider.id version”)                // 2、不同属性项下的相同属性名(如:“TransformService.http://www.w3.org/2001/10/xml-exc-c14n# MechanismType” 和 “TransformService.http://www.w3.org/TR/1999/REC-xpath-19991116 MechanismType”)                key = key.split(" ")[0];                System.out.println("key : " + key + "\nvalue:" + arr[i].get(key) + "\n");            }}
因其打印结果太长,我只截取一部分,想看的筒靴可以自己打印一下,看有多少种算法:

key : Alg.Alias.Signature.SHA1/DSA
value:SHA1withDSA

key : Alg.Alias.Signature.1.2.840.10040.4.3
value:SHA1withDSA

key : Alg.Alias.Signature.DSS
value:SHA1withDSA

…………

key : Signature.SHA512withRSA
value:sun.security.mscapi.RSASignature$SHA512

key : Signature.SHA256withRSA
value:sun.security.mscapi.RSASignature$SHA256

key : KeyStore.Windows-MY
value:sun.security.mscapi.KeyStore$MY

注意:打印出来的包含所有的provide的所有属性,里面包括版本号、提供者名字、算法名字、机制类型、密钥的转换服务等等各种属性及其对应的value,所以我们想要得到我们想要的算法还得再从中查找。

(3)得到提供者中某种算法的名字

看到上面的结果,是否还是有些头疼呢,代码中看见写的算法名都是“AES”,“MD5”什么的,这名字相差甚远,好了,现在我们就来得到我们想要的,直接上代码:
/** * 我们就一摘要算法为例,如果感兴趣的筒靴,还可以试试“Cipher”,或者是上文所提到的“引擎”类试试。 *  * @param serviceType * @return */public static String[] getCryptoImpls(String serviceType) {        Set result = new HashSet();            // 得到所有提供者        Provider[] providers = Security.getProviders();        for (int i=0; i<providers.length; i++) {            // 获得每个提供者提供的服务            Set keys = providers[i].keySet();            // 遍历keys            for (Iterator it=keys.iterator(); it.hasNext(); ) {                String key = (String)it.next();                key = key.split(" ")[0];                // 找到我们想要的算法,并将想要的算法的名字择出来                if (key.startsWith(serviceType+".")) {                    result.add(key.substring(serviceType.length()+1));                } else if (key.startsWith("Alg.Alias."+serviceType+".")) {                    // This is an alias                    result.add(key.substring(serviceType.length()+11));                }            }        }        return (String[])result.toArray(new String[result.size()]);    }
然后,我们在主函数中调用该方法,并传入我们想传的算法“MessageDigest”
public static void main(String[] args) {String[] re = getCryptoImpls("MessageDigest");for (int i = 0; i < re.length; i++) {System.out.println(re[i]);}}
然后我们看看打印结果:

SHA-1
SHA1
SHA-384
OID.1.3.14.3.2.26
2.16.840.1.101.3.4.2.2
SHA
2.16.840.1.101.3.4.2.1
2.16.840.1.101.3.4.2.4
2.16.840.1.101.3.4.2.3
OID.2.16.840.1.101.3.4.2.4
OID.2.16.840.1.101.3.4.2.3
OID.2.16.840.1.101.3.4.2.2
1.3.14.3.2.26
OID.2.16.840.1.101.3.4.2.1
SHA-224
SHA-256
MD2
SHA-512
MD5

有没有很熟悉!相信那些字母的字符串都不陌生,但那些纯数字和点的字符串是什么呢?这些其实也是算法的名字,在其对应的value值中就显示庐山真面目了
(如:key:Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3——value:SHA-512)
眼尖的筒靴很快发现,下面还有一个SHA-512,这不是重复了吗?别忘了,我们检索的是所有提供者的算法,根据上文的Provider中介绍可知,不同的提供者可以有相同的命名算法。

至于“转换名称”,感兴趣的童协可以自行搜索一下,日后有时间我找找资料看看,再写,请见谅!
记得有什么问题请留言!

原创粉丝点击