java ssl连接失败_Javamail在JDK8上无法SSL连接QQ邮箱服务器的解决方案

最近开发了一个工具,其中一个模块的功能是通过javamail发送邮件到指定地址,采用的smtp服务器是QQ邮箱(smtp.qq.com:465)。在本机自测功能的时候都是OK的,但在用户机器上执行发送邮件时却抛错了,查看了下异常信息,报的是javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure,这就意味着在建立ssl连接的时候加密套件协商失败。查看了下双方的JDK版本发现我的机器上运行的是JDK6,而用户运行的是JDK8,JDK8默认禁用了一些被认为不安全的加密套件,而QQ邮箱却强制使用了那些不安全的加密套件。JDK版本的不同导致的差异原因具体可以参见这里。

通过那篇博文提到的方法,我们可以发现要解决这个恶心的QQ邮箱RC4算法不兼容JDK8的问题只能替换JDK目录下的jce配置文件,但这种方法太过于本地化,无法将该解决方案应用到实际客户机上。因此再接着找方案,终于找到一个靠谱的,见这里,大致的意思就是通过反射获取那些定义安全限制的jce类,并做修改。我用这个方法测试后发现的确有效,但期间牵扯出另一个问题,就是从JDK8U102开始,javax.crypto.JceSecurity的isRestricted成员被修饰成了final属性,具体bug描述请见这里,这导致很多网站互相转载的所谓jce反射修改方案实际运行时会抛错:Can not set static final boolean field javax.crypto.JceSecurity.isRestricted to java.lang.Boolean,而我找到的那篇Stackoverflow上的回复却很好的解决了这个问题。

为了让代码看起来更容易理解,以及使用上的方便,我改进了去除JDK8安全限制的方法。JAVA类如下:

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.security.Permission;

import java.security.PermissionCollection;

import java.util.Map;

public class RemoveCryptographyRestrictions {

private volatile static RemoveCryptographyRestrictions INSTANCE=null;

public static void init() throws Exception {

if(INSTANCE==null)

synchronized (RemoveCryptographyRestrictions.class) {

if(INSTANCE==null)

INSTANCE=new RemoveCryptographyRestrictions();

}

}

private RemoveCryptographyRestrictions() throws Exception {

Class> jceSecurity = getClazz("javax.crypto.JceSecurity");

Class> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");

Class> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");

if(jceSecurity==null||cryptoPermissions==null||cryptoAllPermission==null)

return;

setFinalStaticValue(jceSecurity, "isRestricted", false);

PermissionCollection defaultPolicy = getFieldValue(jceSecurity, "defaultPolicy", null, PermissionCollection.class);

Map, ?> map=getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);

map.clear();

Permission permission=getFieldValue(cryptoAllPermission, "INSTANCE", null, Permission.class);

defaultPolicy.add(permission);

}

private Class> getClazz(String className) {

Class> clazz=null;

try {

clazz=Class.forName(className);

} catch (Exception e) {

}

return clazz;

}

private void setFinalStaticValue(Class> srcClazz, String fieldName, Object newValue) throws Exception {

Field field=srcClazz.getDeclaredField(fieldName);

field.setAccessible(true);

Field modifiersField = Field.class.getDeclaredField("modifiers");

modifiersField.setAccessible(true);

modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

field.set(null, newValue);

}

private T getFieldValue(Class> srcClazz, String fieldName, Object owner, Class dstClazz) throws Exception {

Field field=srcClazz.getDeclaredField(fieldName);

field.setAccessible(true);

return dstClazz.cast(field.get(owner));

}

}

使用方法非常简单,只要在你的程序的入口位置加入RemoveCryptographyRestrictions.init();就完成啦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值