使用Google API Java客户端库获取AccessToken时遇证书错误求助
我之前在项目里遇到过完全相同的JVM证书信任问题,给你几个经过实践验证的解决方案,按优先级试试:
解决方案1:确认证书导入的正确性(最常见的坑)
很多时候不是证书本身的问题,而是导入的路径或JDK版本错了:
- 先确定当前应用使用的JDK路径:在代码里加一行
System.out.println(System.getProperty("java.home")),运行后就能看到对应的JDK目录 - 找到JDK的证书库位置:通常是
{java.home}/lib/security/cacerts(Oracle JDK),OpenJDK部分版本是{java.home}/jre/lib/security/cacerts,别搞混了! - 用正确的
keytool命令导入证书(默认密码是changeit):keytool -importcert -file /path/to/your/google-cert.pem -keystore /path/to/confirmed/cacerts -alias google-fcm-trust-cert - 导入时输入
y确认,然后重启你的应用,确保应用加载的是修改后的证书库
解决方案2:在代码中自定义信任证书(推荐多JDK环境)
如果不想修改全局JVM证书,或者你的应用要在不同JDK环境运行,可以在代码里直接指定信任的证书,只影响当前应用:
import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import java.io.FileInputStream; import java.io.File; import java.io.InputStream; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.KeyStore; import java.security.TrustManagerFactory; import java.security.SSLContext; import java.security.SecureRandom; import java.util.Arrays; public static String getAccessToken() throws Exception { // 1. 加载你下载的Google根证书(比如从浏览器导出的.pem文件) CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); File certFile = new File("/path/to/google-root-cert.pem"); InputStream certIn = new FileInputStream(certFile); X509Certificate googleCert = (X509Certificate) certFactory.generateCertificate(certIn); certIn.close(); // 2. 创建自定义信任库并添加证书 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); trustStore.setCertificateEntry("google-fcm-cert", googleCert); // 3. 构建自定义TrustManager和SSLContext TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmFactory.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmFactory.getTrustManagers(), new SecureRandom()); // 4. 配置带自定义SSL的HttpTransport HttpTransport customHttpTransport = new NetHttpTransport.Builder() .setSslSocketFactory(sslContext.getSocketFactory()) .build(); // 5. 用自定义Transport构建GoogleCredential GoogleCredential googleCredential = GoogleCredential .fromStream(new FileInputStream(PATH_JSON_AUTH02), customHttpTransport, new JacksonFactory()) .createScoped(Arrays.asList(URL_SCOPE_FCM)); googleCredential.refreshToken(); return googleCredential.getAccessToken(); }
解决方案3:排查企业代理证书问题
如果你的应用是在企业内网环境,通过代理上网,那问题大概率是代理替换了SSL证书,这时候你需要导入代理的根证书,而不是Google的证书:
- 用浏览器访问
https://oauth2.googleapis.com/token,查看证书详情(不同浏览器操作略有不同),找到代理的根证书并导出 - 按照解决方案1的步骤,把代理根证书导入到JVM的cacerts中
解决方案4:临时禁用证书验证(仅用于测试!)
这个方法非常不安全,绝对不能用于生产环境,只能用来快速确认是否是证书信任问题导致的错误:
import javax.net.ssl.*; import java.security.cert.X509Certificate; // 在调用getAccessToken()之前执行这段代码(仅测试用) static { try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } }
内容的提问来源于stack exchange,提问作者Bruno Silva
相关产品推荐
相关产品推荐

