OpenSSLX509Certificate: mark mContext as transient

Since mContext should not participate in the serialization process,
hide it with the transient qualifier. This will prevent the field from
initialization during the unserialization of this class. Then of course
the instance will be in a valid state.

(cherry picked from commit 8d57b9dbbd883422a0ff02083bfcf637b097e504)

Bug: 21437603
Change-Id: Ie9453c16d11820a91caff92c3f7b326d12f8a8f4
diff --git a/src/main/java/org/conscrypt/OpenSSLX509Certificate.java b/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
index e3d8b01..1939de1 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
@@ -52,7 +52,7 @@
 import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
 
 public class OpenSSLX509Certificate extends X509Certificate {
-    private final long mContext;
+    private transient final long mContext;
 
     OpenSSLX509Certificate(long ctx) {
         mContext = ctx;
diff --git a/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java b/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java
new file mode 100644
index 0000000..626b76c
--- /dev/null
+++ b/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Field;
+
+import junit.framework.TestCase;
+
+public class OpenSSLX509CertificateTest extends TestCase {
+    public void testSerialization_NoContextDeserialization() throws Exception {
+        // Set correct serialVersionUID
+        {
+            ObjectStreamClass clDesc = ObjectStreamClass.lookup(OpenSSLX509Certificate.class);
+            assertNotNull(clDesc);
+
+            // Set our fake class's serialization UID.
+            Field targetUID = ZpenSSLX509Certificate.class.getDeclaredField("serialVersionUID");
+            targetUID.setAccessible(true);
+            targetUID.set(null, clDesc.getSerialVersionUID());
+        }
+
+        final byte[] impostorBytes;
+        // Serialization
+        {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(new ZpenSSLX509Certificate(0xA5A5A5A5A5A5A5A5L));
+            oos.close();
+            impostorBytes = baos.toByteArray();
+        }
+
+        // Fix class name
+        {
+            boolean fixed = false;
+            for (int i = 0; i < impostorBytes.length - 4; i++) {
+                if (impostorBytes[i] == 'Z' && impostorBytes[i + 1] == 'p'
+                        && impostorBytes[i + 2] == 'e' && impostorBytes[i + 3] == 'n') {
+                    impostorBytes[i] = 'O';
+                    fixed = true;
+                    break;
+                }
+            }
+            assertTrue(fixed);
+        }
+
+        // Deserialization
+        {
+            ByteArrayInputStream bais = new ByteArrayInputStream(impostorBytes);
+            ObjectInputStream ois = new ObjectInputStream(bais);
+            OpenSSLX509Certificate cert = (OpenSSLX509Certificate) ois.readObject();
+            ois.close();
+            assertEquals(0L, cert.getContext());
+        }
+    }
+}
diff --git a/src/test/java/org/conscrypt/ZpenSSLX509Certificate.java b/src/test/java/org/conscrypt/ZpenSSLX509Certificate.java
new file mode 100644
index 0000000..ea3870a
--- /dev/null
+++ b/src/test/java/org/conscrypt/ZpenSSLX509Certificate.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.Serializable;
+
+/**
+ * This is a fake class to test de-serialization with malicious payloads.
+ */
+public class ZpenSSLX509Certificate implements Serializable {
+    /** This will be set via reflection in the test. */
+    private static final long serialVersionUID = 0L;
+
+    public final long mContext;
+
+    ZpenSSLX509Certificate(long ctx) {
+        mContext = ctx;
+    }
+}