My Android app is a MMORPG so it connects to my server. For some reason, this is breaking AdMob SSV. Ads show fine (test and real), but the server-side validation callback is never received. This is driving me insane - appreciate any help.
Here is the warning message I see in the logcat:
Precache exception
hi: javax.net.ssl.SSLProtocolException: Read error: ssl=0x77c5e4c0b798: Failure in SSL library, usually a protocol error
error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT (external/boringssl/src/crypto/fipsmodule/cipher/e_aes.c:1095 0x77c538ad22dc:0x00000000)
error:1000008b:SSL routines:OPENSSL_internal:DECRYPTION_FAILED_OR_BAD_RECORD_MAC (external/boringssl/src/ssl/tls_record.cc:298 0x77c538ad22dc:0x00000000)
at hd.a(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:6)
at hq.a(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:0)
at xv.b(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:2)
at xv.a(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:2)
at tx.t(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:4)
at yr.a(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:0)
at zt.m(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:2)
at zt.l(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:31)
at zt.j(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:39)
at zt.d(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:30)
at wc.run(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:43)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
Caused by: javax.net.ssl.SSLProtocolException: Read error: ssl=0x77c5e4c0b798: Failure in SSL library, usually a protocol error
error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT (external/boringssl/src/crypto/fipsmodule/cipher/e_aes.c:1095 0x77c538ad22dc:0x00000000)
error:1000008b:SSL routines:OPENSSL_internal:DECRYPTION_FAILED_OR_BAD_RECORD_MAC (external/boringssl/src/ssl/tls_record.cc:298 0x77c538ad22dc:0x00000000)
at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1079)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:858)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.readUntilDataAvailable(ConscryptEngineSocket.java:824)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.read(ConscryptEngineSocket.java:797)
at com.android.okhttp.okio.Okio$2.read(Okio.java:138)
at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:213)
at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:51)
at com.android.okhttp.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java:395)
at com.android.okhttp.okio.RealBufferedSource$1.read(RealBufferedSource.java:372)
at hd.a(:com.google.android.gms.policy_ads_fdr_dynamite@231710101@231710100057.526733554.526733554:3)
... 13 more
Here is my client side SSL connection code:
//load your key store as a stream and initialize a KeyStore
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
//if your store is password protected then declare it (it can be null however)
String tsName = "res/gamedata/truststore-bks";
char[] trustPassword = "REDACTED".toCharArray();
//load the stream to your store
trustStore.load(MyCommandReceiver.GetActiveActivity().getAssets().open(tsName), trustPassword);
//initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
//get the trust managers from the factory
TrustManager[] trustManagers = trustFactory.getTrustManagers();
//initialize an ssl context to use these managers and set as default
//SSLContext sslContext = SSLContext.getInstance("SSL");
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, trustManagers, null);
//SSLContext.setDefault(sslContext); //this was breaking admob ads from loading - and likely other stuff too
//create sslfactory from the ssl context
SSLSocketFactory factory = sslContext.getSocketFactory();
final int connectTimeout = 6 * 1000; //6 seconds to connect
if (GameActivity.isPROD) {
//https://stackoverflow.com/questions/5715751/ssl-socket-connect-timeout
//GameActivity.sC = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); //the way code wanted me to do it
GameActivity.sC = (SSLSocket) factory.createSocket();
GameActivity.sC.setSoTimeout(connectTimeout);
GameActivity.sC.connect(new InetSocketAddress(GameActivity.PROD_IP, GameActivity.PROD_PORT), connectTimeout);
} else {
GameActivity.ShowLongToast("===DEV SERVER===");
//https://stackoverflow.com/questions/5715751/ssl-socket-connect-timeout
//GameActivity.sC = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); //the way code wanted me to do it
GameActivity.sC = (SSLSocket) factory.createSocket();
GameActivity.sC.setSoTimeout(connectTimeout);
GameActivity.sC.connect(new InetSocketAddress(GameActivity.DEV_IP, GameActivity.PROD_PORT), connectTimeout);
}
//GameActivity.sC.setEnabledProtocols(new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"});
//GameActivity.sC.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
GameActivity.sC.setEnabledProtocols(new String[]{"TLSv1.3"});
//even tried with line above commented out
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer: ------SUPPORTED CIPHER SUITES------");
GameActivity.LoggerWrite("i", TAG, Arrays.toString(factory.getSupportedCipherSuites()));
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:------SOCKET INFO------");
printSocketInfo(GameActivity.sC);
GameActivity.sC.setSoTimeout(0);
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:------START HANDSHAKE------");
GameActivity.sC.startHandshake();
GameActivity.sC.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(HandshakeCompletedEvent arg0) {
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:------HANDSHAKE-COMPLETED-LISTENER - START------");
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Cipher suite: " + arg0.getCipherSuite());
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Local Principal: " + arg0.getLocalPrincipal());
Certificate[] peerCertificates = null;
try {
peerCertificates = arg0.getPeerCertificates();
for (Certificate s : peerCertificates) {
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Peer Certificate(public key): " + s.getPublicKey());
}
} catch (SSLPeerUnverifiedException e) {
GameActivity.LoggerWrite("e", TAG, "LoginActivity:connectToServer:arg0.getPeerCertificates()" + e);
}
try {
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Peer Principal: " + arg0.getPeerPrincipal());
} catch (SSLPeerUnverifiedException e) {
GameActivity.LoggerWrite("e", TAG, "LoginActivity:connectToServer:arg0.getPeerPrincipal()" + e);
}
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Session: " + arg0.getSession());
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Socket: " + arg0.getSocket());
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:Source: " + arg0.getSource());
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:------HANDSHAKE-COMPLETED-LISTENER - END------");
// connectedToServer = true; //so subsequent clicks to Login don't make new connections
}
});
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:------START DataStreams - START------");
GameActivity.commandIn = new DataInputStream(GameActivity.sC.getInputStream()); //create input-stream for a command socket
GameActivity.commandOut = new DataOutputStream(GameActivity.sC.getOutputStream()); //create output-stream for a command socket
GameActivity.LoggerWrite("i", TAG, "LoginActivity:connectToServer:------START DataStreams - END------");
//creating a thread for receiving commands
MyCommandReceiver.GetActiveActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
GameActivity.commandReceiver = new MyCommandReceiver();
GameActivity.commandReceiverThread = new Thread(null, GameActivity.commandReceiver, "commandReceiver-" + ""); //characterName
GameActivity.commandReceiverThread.setDaemon(true);
GameActivity.commandReceiverThread.start();
}
});