JCE provider管理的问题

本文深入探讨了在Java Cryptography Extension (JCE)框架中,不同模块依赖同一Provider(如Infosecurity)的不同版本时出现的ClassCastException等问题。通过分析MessageDigest.getInstance方法的实现细节,揭示了JCE内部管理Provider的机制,并提出了通过自定义Provider管理逻辑来解决冲突的具体方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[b]现象[/b]
两个module A和B分别采用了infosec的不同版本,虽然classloader已经保证了A和B所依赖的资源均可以正确的load, 并且成功地newInstance() 或者new构造,但是仍然出现ClassCastException、ClassNotFoundException、NoSuchMethodException等异常

[b]问题分析[/b]
Security vendor有infosecurity,bouncycastle等好几种,在JCE框架里面都被抽象成Provider,每个供应商实现抽象类Provider,JVM会管理这些Provider.同一个JVM中,不同的module依赖了同一个Provider(eg.Infosecurity)的不同版本,如果Provider之间是不兼容的,尽管通过ClassLoader机制让不通的module分别load到了各自对应的Provider,但是因为JCE管理Provider的方式,它们的name完全相同,B的会把A的infosec Provider给覆盖。这会导致A使用了B的provider,仍然会产生可恶的ClassCastException、NoSuchMethodException等。

[b]问题探索[/b]
一步步的看Provider是被管理使用的。首先从MessageDigest开始,一般我们这么使用
[code]
MessageDigest md = MessageDigest.getInstance("SHA-1","INFOSEC");
//or
MessageDigest md = MessageDigest.getInstance("MD5");[/code]
看看里面怎么做的,很明显,找到多个取第一个。
  public static MessageDigest getInstance(String paramString)
throws NoSuchAlgorithmException
{
try
{
Object[] arrayOfObject = Security.getImpl(paramString, "MessageDigest", (String)null);
if (arrayOfObject[0] instanceof MessageDigest)
{
localObject = (MessageDigest)arrayOfObject[0];
((MessageDigest)localObject).provider = ((Provider)arrayOfObject[1]);
return localObject;
}
Object localObject = new Delegate((MessageDigestSpi)arrayOfObject[0], paramString);
((MessageDigest)localObject).provider = ((Provider)arrayOfObject[1]);
return localObject;
}
catch (NoSuchProviderException localNoSuchProviderException)
{
throw new NoSuchAlgorithmException(paramString + " not found");
}
}

然后就到Security类了
  static Object[] getImpl(String paramString1, String paramString2, String paramString3)
throws NoSuchAlgorithmException, NoSuchProviderException
{
if (paramString3 == null)
return GetInstance.getInstance(paramString2, getSpiClass(paramString2), paramString1).toArray();
return GetInstance.getInstance(paramString2, getSpiClass(paramString2), paramString1, paramString3).toArray();
}

getSpiClass方法暂时忽略掉,这里有2个分支,为了方便直接看第二个分支。
  public static Instance getInstance(String paramString1, Class paramClass, String paramString2, String paramString3)
throws NoSuchAlgorithmException, NoSuchProviderException
{
return getInstance(getService(paramString1, paramString2, paramString3), paramClass);
}

public static Provider.Service getService(String paramString1, String paramString2, String paramString3)
throws NoSuchAlgorithmException, NoSuchProviderException
{
if ((paramString3 == null) || (paramString3.length() == 0))
throw new IllegalArgumentException("missing provider");
Provider localProvider = Providers.getProviderList().getProvider(paramString3);
if (localProvider == null)
throw new NoSuchProviderException("no such provider: " + paramString3);
Provider.Service localService = localProvider.getService(paramString1, paramString2);
if (localService == null)
throw new NoSuchAlgorithmException("no such algorithm: " + paramString2 + " for provider " + paramString3);
return localService;
}


可以看到,最终的是在Providers所管理的ProviderList里面保存所有的Provider,而且保存的方式是以paramString3参数,也就是"INFOSEC"去获取Provider。而相同vendor的不同版本Provider的Name是完全一样的,区别是version不同,这里印证了猜想:[color=red]不同的Provider因为相同的name而被认为是同一个Provider而产生的同名覆盖[/color]

如何解决。Security,Providers,ProviderList是java core包的类,不可轻易改动,否则后果很难估计。我在这里采用的方法比较简陋,从Providers类做改动,直接覆盖Providers类的管理逻辑:针对当前Thread的classloader的类型做判断,如果是SystemClassLoader,则使用自己的Provider管理逻辑,否则还是使用之前的管理方式。

完成后对Providers重新打包成providers.jar,加入启动参数-Xbootclasspath/p:/data/overwrite/providers.jar以覆盖rt.jar中的Providers类。大功告成。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值