Java密码体系结构(JCA)是一个可扩展的框架,使您能够使用执行加密操作。 JCA还促进实现独立性(程序不应该在乎谁提供加密服务)和实现互操作性(程序不应该与特定加密服务的特定提供者联系在一起)。
JCA允许将许多密码服务(例如密码,密钥生成器,消息摘要)捆绑到java.security.Provider类中,并声明式地注册在特殊文件(java.security)中,或者通过java.security.Security类以编程方式(方法)进行注册。 'addProvider')。
尽管JCA是标准,但是不同的JDK实施JCA的方式有所不同。 在Sun / Oracle与IBM JDK之间,IBM JDK比Oracle更“有序”。 例如,IBM的超级提供程序(com.ibm.crypto.provider.IBMJCE)实现以下密钥库格式:JCEKS,PKCS12KS(PKCS12),JKS。 Oracle JDK将密钥库格式实现“传播”到以下提供程序中:
- sun.security.provider.Sun – JKS
- com.sun.crypto.provider.SunJCE – JCEKS
- com.sun.net.ssl.internal.ssl.Provider – PKCS12
尽管流行的建议是编写不指向特定Provider类的应用程序,但是在某些用例中,需要应用程序/程序确切了解Provider类提供的服务。 当支持可能与特定JDK(例如与IBM JDK捆绑在一起的WebSphere)紧密耦合的多个应用程序服务器时,此要求变得更加普遍。 我通常使用Tomcat + Oracle JDK进行开发(更轻便,更快),但是我的测试/生产设置是WebSphere + IBM JDK。 为了使事情更加复杂,我的项目需要使用硬件安全模块(HSM),该模块通过提供程序类com.ncipher.provider.km.nCipherKM使用JCA API。 因此,当我在家(无法访问HSM)时,我想继续编写代码,但至少要在JDK提供程序上对代码进行测试。 然后,在将代码提交给源代码管理之前,我可以切换为使用nCipherKM提供程序进行另一轮单元测试。
通常的假设是,一个提供程序类就足够了,例如对于IBM JDK来说是IBMJCE,对于Oracle JDK是SunJCE。 因此,通常的解决方案是实现一个指定一个提供程序的类,并使用反射来避免由于“未找到类”而导致的编译错误:
//For nShield HSM
Class c = Class.forName('com.ncipher.provider.km.nCipherKM');
Provider provider = (Provider)c.newInstance();//For Oracle JDK
Class c = Class.forName('com.sun.crypto.provider.SunJCE');
Provider provider = (Provider)c.newInstance();//For IBM JDK
Class c = Class.forName('com.ibm.crypto.provider.IBMJCE');
Provider provider = (Provider)c.newInstance();
这种设计是可以的,直到我在Oracle JDK上运行一些单元测试用例时遇到NoSuchAlgorithmException错误。 我使用的算法是RSA,这是一种常见算法! 怎么可能,文档说支持RSA! 相同的测试用例在IBM JDK上运行良好。
经过进一步调查,我感到非常沮丧的是,SunJCE提供程序没有用于RSA的KeyPairGenerator服务的实现。 但是,可以在提供程序类sun.security.rsa.SunRsaSign中找到一个实现。 因此,“ 1提供者全部提供”的假设被打破了。 但是由于有了JCA的开放API,在请求Service实例时可以传递Provider对象,例如
KeyGenerator kgen = KeyGenerator.getInstance('AES', provider);
为了帮助检查各种Provider对象,我提供了一个JUnit测试,以漂亮地打印JDK中每个已注册Provider实例的各种服务。
package org.gizmo.jca;import java.security.Provider;
import java.security.Provider.Service;
import java.security.Security;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;import javax.crypto.KeyGenerator;import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;public class CryptoTests {@Testpublic void testBouncyCastleProvider() throws Exception {Provider p = new BouncyCastleProvider();String info = p.getInfo();System.out.println(p.getClass() + ' - ' + info);printServices(p);}@Testpublic void testProviders() throws Exception {Provider[] providers = Security.getProviders();for(Provider p : providers) {String info = p.getInfo();System.out.println(p.getClass() + ' - ' + info);printServices(p);}}private void printServices(Provider p) {SortedSetservices = new TreeSet(new ProviderServiceComparator());services.addAll(p.getServices());for(Service service : services) {String algo = service.getAlgorithm();System.out.println('==> Service: ' + service.getType() + ' - ' + algo);}}/*** This is to sort the various Services to make it easier on the eyes...*/private class ProviderServiceComparator implements Comparator{@Overridepublic int compare(Service object1, Service object2) {String s1 = object1.getType() + object1.getAlgorithm();String s2 = object2.getType() + object2.getAlgorithm();;return s1.compareTo(s2);}}
}
无论如何,如果您使用的算法通用并且足够强大,可以满足您的需求,那么可以使用BouncyCastle提供程序。 它在所有JDK(针对IBM和Oracle进行了测试)上都能很好地工作。 BouncyCastle不支持JKS或JCEKS密钥库格式,但是如果您不太挑剔,则BC密钥库格式可以正常工作。 BouncyCastle也是开源的,可以免费包含在您的应用程序中。
提示 :JKS密钥库无法存储SecretKeys。 您可以尝试做功课
希望本文能启发您进一步探索JCA,或者至少在与JCA合作时意识到“幸福的无知”的陷阱。
参考: YK研讨会的博客中, 使用JCA的密码术-来自我们JCG合作伙伴 Allen Julia的“ 提供者中的服务” 。
翻译自: https://www.javacodegeeks.com/2013/03/cryptography-using-jca-services-in-providers.html