javax.net.ssl.SSLHandshakeException - Received fatal alert - handshake_failure 邮件发送失败

2017, Jan 17    

今天在写邮件发送的时候,报了一个错误javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure。 看字面上意思应该是握手失败。查了一些资料,是因为Java8开始禁用了 RC4 加密方式。而刚刚好,QQ的服务器用的就是这种方式。 这就很尴尬了,最后找到两个方法来解决这个问题。

这种方法比较简单, 之间在发送邮件之前执行下列方法即可, 这个方法会判断如果是Oracle的jre,就会通过反射跳过JCE API的执行。

public static void removeCryptographyRestrictions() {
      if (!isRestrictedCryptography()) {
          return;
      }
      try {
          /*
           * Do the following, but with reflection to bypass access checks:
           *
           * JceSecurity.isRestricted = false;
           * JceSecurity.defaultPolicy.perms.clear();
           * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
           */
          final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
          final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
          final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

          final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
          isRestrictedField.setAccessible(true);
          final Field modifiersField = Field.class.getDeclaredField("modifiers");
          modifiersField.setAccessible(true);
          modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
          isRestrictedField.set(null, false);

          final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
          defaultPolicyField.setAccessible(true);
          final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

          final Field perms = cryptoPermissions.getDeclaredField("perms");
          perms.setAccessible(true);
          ((Map<?, ?>) perms.get(defaultPolicy)).clear();

          final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
          instance.setAccessible(true);
          defaultPolicy.add((Permission) instance.get(null));

      } catch (final Exception e) {
      }
  }

  private static boolean isRestrictedCryptography() {
      // This simply matches the Oracle JRE, but not OpenJDK.
      return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));
  }

方法二 修改jre的java.security文件 参考: 文章

这个方法是说修改 %JAVA_HOME%/jre/lib/java.security 文件 将jdk.tls.disabledAlgorithms=SSLv3, RC4, DH keySize < 768 替换为 jdk.tls.disabledAlgorithms=SSLv3, DH keySize < 768jdk.tls.legacyAlgorithms 这个参数里的 RC4_128, RC4_40 删除。

这种方法局限性比较大,因为要修改本地jre内的文件,所以我用的是 方法一 来处理这个问题。

希望这篇文章可以帮到大家。