PHP连接MySQL时Android端SSL证书缺失:创建与配置方法问询
解决Android调用PHP HTTPS接口时的SSLHandshakeException错误
一、先搞清楚:给哪配证书?
这个错误是Android和运行PHP的Web服务器(Apache/Nginx这类)建立HTTPS连接时,不信任服务器的SSL证书导致的,和MySQL数据库半毛钱关系都没有——PHP和MySQL的SSL连接是后端内部的事,Android根本管不着。你要做的是给Web服务器配SSL证书,让Android认它。
二、生成自签名证书(测试环境用)
生产环境建议用正规CA签发的免费证书(比如Let's Encrypt),测试环境自己用OpenSSL生成就行:
- 确保你装了OpenSSL(Linux基本自带,Windows自己下OpenSSL工具包)
- 依次跑这三条命令:
- 生成服务器私钥:
openssl genrsa -out server.key 2048 - 生成证书签名请求(按提示填信息,Common Name必须填你的服务器IP/域名,比如你用的127.0.0.1):
openssl req -new -key server.key -out server.csr - 生成自签名证书(有效期365天):
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
- 生成服务器私钥:
三、给Web服务器配置HTTPS
1. Apache配置
- 找到Apache的SSL配置文件(一般叫
httpd-ssl.conf或ssl.conf) - 加这几行配置:
SSLEngine on SSLCertificateFile /你放证书的路径/server.crt SSLCertificateKeyFile /你放私钥的路径/server.key - 启用SSL模块(Linux跑
a2enmod ssl),然后重启Apache。
2. Nginx配置
- 打开你的站点配置文件(比如
/etc/nginx/sites-available/你的项目.conf) - 加一个HTTPS的server块:
server { listen 443 ssl; server_name 127.0.0.1; # 必须和证书的Common Name一致 ssl_certificate /你放证书的路径/server.crt; ssl_certificate_key /你放私钥的路径/server.key; # 下面是你的项目基础配置,比如PHP解析 root /你的PHP项目根目录; location ~ \.php$ { fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 按你的PHP版本改 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } - 重启Nginx生效。
四、Android端怎么处理证书?
方法1:把证书放进项目,让Android信任它(推荐)
- 把生成的
server.crt重命名为server.cer,放到Android项目的app/src/main/res/raw目录(没有raw目录就自己建一个)。 - 写个工具类生成自定义的SSLSocketFactory:
import android.content.Context; import java.io.InputStream; import java.security.KeyStore; import java.security.cert.CertificateFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; public class SSLUtils { public static SSLSocketFactory getSSLSocketFactory(Context context) { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); InputStream certStream = context.getResources().openRawResource(R.raw.server); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); keyStore.setCertificateEntry("server", certFactory.generateCertificate(certStream)); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); return sslContext.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); return null; } } } - 修改你的
PutData类,让它用这个自定义的SSLSocketFactory。假设PutData里用的是HttpURLConnection,找到打开连接的地方,改成:HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(); httpsConn.setSSLSocketFactory(SSLUtils.getSSLSocketFactory(你的Context实例)); // 比如传Activity.this
方法2:测试环境临时跳过SSL验证(别放生产!)
如果只是临时测试,不想搞证书信任的话,可以直接绕过SSL检查,但生产环境绝对不能这么干:
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; public class SSLSkipUtils { public static void disableSSLCheck() { try { TrustManager[] trustAll = 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, trustAll, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } } }
在调用putData.startPut()之前,先跑SSLSkipUtils.disableSSLCheck();就行。
内容的提问来源于stack exchange,提问作者user21655182
相关产品推荐
相关产品推荐

