admin管理员组文章数量:1663678
最近开发了一个工具,其中一个模块的功能是通过javamail发送邮件到指定地址,采用的smtp服务器是QQ邮箱(smtp.qq:465)。在本机自测功能的时候都是OK的,但在用户机器上执行发送邮件时却抛错了,查看了下异常信息,报的是javax.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();就完成啦~
版权声明:本文标题:java ssl连接失败_Javamail在JDK8上无法SSL连接QQ邮箱服务器的解决方案 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1730000226a1218755.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论