Retain original encoded form in certs returned by PKCS7.

This changes sun.security.pkcs.PKCS7 so that the X509Certificate
instances it returns provide the original encoded form of the
certificate via Certificate.getEncoded. Prior to this change,
Certificate.getEncoded of these instances returned the DER form
of the certificate.

Returning the DER form is normally a good idea, but causes trouble
when this sun.security.pkcs.PKCS7 is used for parsing APKs' JAR
signatures. The way Android works is that an APK is permitted to be
updated only if the encoded form of the update's signing certificate
is exactly the same as the one of the already installed version of the
APK. Some APKs use signing certificates which are not DER-encoded,
which will lead to updates of such APKs to be rejected without this
fix.

Bug: 30148997
Change-Id: I44edc63d4923f8fb4f8f7a087611a7496137db52
diff --git a/ojluni/src/main/java/sun/security/pkcs/PKCS7.java b/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
index 4929c01..6b95214 100755
--- a/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
+++ b/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
@@ -31,11 +31,17 @@
 import java.util.*;
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.X509CRL;
 import java.security.cert.CRLException;
 import java.security.cert.CertificateFactory;
 import java.security.*;
 
+import javax.security.auth.x500.X500Principal;
+
 import sun.security.util.*;
 import sun.security.x509.AlgorithmId;
 import sun.security.x509.CertificateIssuerName;
@@ -214,13 +220,14 @@
         for (int i=0; i < contents.length; i++) {
             ByteArrayInputStream bais = null;
             try {
+                byte[] original = contents[i].getOriginalEncodedForm();
                 if (certfac == null)
-                    certificates[i] = new X509CertImpl(contents[i]);
+                    certificates[i] = new X509CertImpl(contents[i], original);
                 else {
-                    byte[] encoded = contents[i].toByteArray();
-                    bais = new ByteArrayInputStream(encoded);
-                    certificates[i] =
-                        (X509Certificate)certfac.generateCertificate(bais);
+                    bais = new ByteArrayInputStream(original);
+                    certificates[i] = new VerbatimX509Certificate(
+                        (X509Certificate)certfac.generateCertificate(bais),
+                        original);
                     bais.close();
                     bais = null;
                 }
@@ -279,7 +286,7 @@
          * (certificates are OPTIONAL)
          */
         if ((byte)(dis.peekByte()) == (byte)0xA0) {
-            DerValue[] certVals = dis.getSet(2, true);
+            DerValue[] certVals = dis.getSet(2, true, true);
 
             len = certVals.length;
             certificates = new X509Certificate[len];
@@ -292,13 +299,14 @@
                     // We only parse the normal certificate. Other types of
                     // CertificateChoices ignored.
                     if (tag == DerValue.tag_Sequence) {
+                        byte[] original = certVals[i].getOriginalEncodedForm();
                         if (certfac == null) {
-                            certificates[count] = new X509CertImpl(certVals[i]);
+                            certificates[count] = new X509CertImpl(certVals[i], original);
                         } else {
-                            byte[] encoded = certVals[i].toByteArray();
-                            bais = new ByteArrayInputStream(encoded);
-                            certificates[count] =
-                                (X509Certificate)certfac.generateCertificate(bais);
+                            bais = new ByteArrayInputStream(original);
+                            certificates[count] = new VerbatimX509Certificate(
+                                (X509Certificate)certfac.generateCertificate(bais),
+                                original);
                             bais.close();
                             bais = null;
                         }
@@ -408,13 +416,14 @@
         for (int i = 0; i < len; i++) {
             ByteArrayInputStream bais = null;
             try {
+                byte[] original = certVals[i].getOriginalEncodedForm();
                 if (certfac == null)
-                    certificates[i] = new X509CertImpl(certVals[i]);
+                    certificates[i] = new X509CertImpl(certVals[i], original);
                 else {
-                    byte[] encoded = certVals[i].toByteArray();
-                    bais = new ByteArrayInputStream(encoded);
-                    certificates[i] =
-                        (X509Certificate)certfac.generateCertificate(bais);
+                    bais = new ByteArrayInputStream(original);
+                    certificates[i] = new VerbatimX509Certificate(
+                        (X509Certificate)certfac.generateCertificate(bais),
+                        original);
                     bais.close();
                     bais = null;
                 }
@@ -762,4 +771,196 @@
     public boolean isOldStyle() {
         return this.oldStyle;
     }
+
+    /**
+     * For legacy reasons we need to return exactly the original encoded certificate bytes, instead
+     * of letting the underlying implementation have a shot at re-encoding the data.
+     */
+    private static class VerbatimX509Certificate extends WrappedX509Certificate {
+        private byte[] encodedVerbatim;
+
+        public VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
+            super(wrapped);
+            this.encodedVerbatim = encodedVerbatim;
+        }
+
+        @Override
+        public byte[] getEncoded() throws CertificateEncodingException {
+            return encodedVerbatim;
+        }
+    }
+
+    private static class WrappedX509Certificate extends X509Certificate {
+        private final X509Certificate wrapped;
+
+        public WrappedX509Certificate(X509Certificate wrapped) {
+            this.wrapped = wrapped;
+        }
+
+        @Override
+        public Set<String> getCriticalExtensionOIDs() {
+            return wrapped.getCriticalExtensionOIDs();
+        }
+
+        @Override
+        public byte[] getExtensionValue(String oid) {
+            return wrapped.getExtensionValue(oid);
+        }
+
+        @Override
+        public Set<String> getNonCriticalExtensionOIDs() {
+            return wrapped.getNonCriticalExtensionOIDs();
+        }
+
+        @Override
+        public boolean hasUnsupportedCriticalExtension() {
+            return wrapped.hasUnsupportedCriticalExtension();
+        }
+
+        @Override
+        public void checkValidity()
+                throws CertificateExpiredException, CertificateNotYetValidException {
+            wrapped.checkValidity();
+        }
+
+        @Override
+        public void checkValidity(Date date)
+                throws CertificateExpiredException, CertificateNotYetValidException {
+            wrapped.checkValidity(date);
+        }
+
+        @Override
+        public int getVersion() {
+            return wrapped.getVersion();
+        }
+
+        @Override
+        public BigInteger getSerialNumber() {
+            return wrapped.getSerialNumber();
+        }
+
+        @Override
+        public Principal getIssuerDN() {
+            return wrapped.getIssuerDN();
+        }
+
+        @Override
+        public Principal getSubjectDN() {
+            return wrapped.getSubjectDN();
+        }
+
+        @Override
+        public Date getNotBefore() {
+            return wrapped.getNotBefore();
+        }
+
+        @Override
+        public Date getNotAfter() {
+            return wrapped.getNotAfter();
+        }
+
+        @Override
+        public byte[] getTBSCertificate() throws CertificateEncodingException {
+            return wrapped.getTBSCertificate();
+        }
+
+        @Override
+        public byte[] getSignature() {
+            return wrapped.getSignature();
+        }
+
+        @Override
+        public String getSigAlgName() {
+            return wrapped.getSigAlgName();
+        }
+
+        @Override
+        public String getSigAlgOID() {
+            return wrapped.getSigAlgOID();
+        }
+
+        @Override
+        public byte[] getSigAlgParams() {
+            return wrapped.getSigAlgParams();
+        }
+
+        @Override
+        public boolean[] getIssuerUniqueID() {
+            return wrapped.getIssuerUniqueID();
+        }
+
+        @Override
+        public boolean[] getSubjectUniqueID() {
+            return wrapped.getSubjectUniqueID();
+        }
+
+        @Override
+        public boolean[] getKeyUsage() {
+            return wrapped.getKeyUsage();
+        }
+
+        @Override
+        public int getBasicConstraints() {
+            return wrapped.getBasicConstraints();
+        }
+
+        @Override
+        public byte[] getEncoded() throws CertificateEncodingException {
+            return wrapped.getEncoded();
+        }
+
+        @Override
+        public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+                InvalidKeyException, NoSuchProviderException, SignatureException {
+            wrapped.verify(key);
+        }
+
+        @Override
+        public void verify(PublicKey key, String sigProvider)
+                throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+                NoSuchProviderException, SignatureException {
+            wrapped.verify(key, sigProvider);
+        }
+
+        @Override
+        public String toString() {
+            return wrapped.toString();
+        }
+
+        @Override
+        public PublicKey getPublicKey() {
+            return wrapped.getPublicKey();
+        }
+
+        @Override
+        public List<String> getExtendedKeyUsage() throws CertificateParsingException {
+            return wrapped.getExtendedKeyUsage();
+        }
+
+        @Override
+        public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
+            return wrapped.getIssuerAlternativeNames();
+        }
+
+        @Override
+        public X500Principal getIssuerX500Principal() {
+            return wrapped.getIssuerX500Principal();
+        }
+
+        @Override
+        public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
+            return wrapped.getSubjectAlternativeNames();
+        }
+
+        @Override
+        public X500Principal getSubjectX500Principal() {
+            return wrapped.getSubjectX500Principal();
+        }
+
+        @Override
+        public void verify(PublicKey key, Provider sigProvider) throws CertificateException,
+                NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+            wrapped.verify(key, sigProvider);
+        }
+    }
 }
diff --git a/ojluni/src/main/java/sun/security/util/DerInputBuffer.java b/ojluni/src/main/java/sun/security/util/DerInputBuffer.java
index 786e791..b375c60 100755
--- a/ojluni/src/main/java/sun/security/util/DerInputBuffer.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputBuffer.java
@@ -71,6 +71,16 @@
         return retval;
     }
 
+    int getPos() {
+        return pos;
+    }
+
+    byte[] getSlice(int startPos, int size) {
+        byte[] result = new byte[size];
+        System.arraycopy(buf, startPos, result, 0, size);
+        return result;
+    }
+
     int peek() throws IOException {
         if (pos >= count)
             throw new IOException("out of data");
diff --git a/ojluni/src/main/java/sun/security/util/DerInputStream.java b/ojluni/src/main/java/sun/security/util/DerInputStream.java
index e0f77ee..f815d68 100755
--- a/ojluni/src/main/java/sun/security/util/DerInputStream.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputStream.java
@@ -328,13 +328,22 @@
      */
     public DerValue[] getSet(int startLen, boolean implicit)
         throws IOException {
+        return getSet(
+            startLen,
+            implicit,
+            false); // no need to retain original encoded form
+    }
+
+    public DerValue[] getSet(int startLen, boolean implicit,
+            boolean originalEncodedFormRetained)
+        throws IOException {
         tag = (byte)buffer.read();
         if (!implicit) {
             if (tag != DerValue.tag_Set) {
                 throw new IOException("Set tag error");
             }
         }
-        return (readVector(startLen));
+        return (readVector(startLen, originalEncodedFormRetained));
     }
 
     /*
@@ -343,6 +352,18 @@
      * this same helper routine.
      */
     protected DerValue[] readVector(int startLen) throws IOException {
+        return readVector(
+            startLen,
+            false); // no need to retain original encoded form
+    }
+
+    /*
+     * Read a "vector" of values ... set or sequence have the
+     * same encoding, except for the initial tag, so both use
+     * this same helper routine.
+     */
+    protected DerValue[] readVector(int startLen,
+            boolean originalEncodedFormRetained) throws IOException {
         DerInputStream  newstr;
 
         byte lenByte = (byte)buffer.read();
@@ -387,7 +408,7 @@
         DerValue value;
 
         do {
-            value = new DerValue(newstr.buffer);
+            value = new DerValue(newstr.buffer, originalEncodedFormRetained);
             vec.addElement(value);
         } while (newstr.available() > 0);
 
diff --git a/ojluni/src/main/java/sun/security/util/DerValue.java b/ojluni/src/main/java/sun/security/util/DerValue.java
index beb51c1..6bca1f7 100755
--- a/ojluni/src/main/java/sun/security/util/DerValue.java
+++ b/ojluni/src/main/java/sun/security/util/DerValue.java
@@ -72,6 +72,12 @@
 
     private int                 length;
 
+    /**
+     * The original encoded form of the whole value (tag, length, and value)
+     * or null if the form was not provided or was not retained during parsing.
+     */
+    private byte[]              originalEncodedForm;
+
     /*
      * The type starts at the first byte of the encoding, and
      * is one of these tag_* values.  That may be all the type
@@ -243,10 +249,12 @@
     /*
      * package private
      */
-    DerValue(DerInputBuffer in) throws IOException {
+    DerValue(DerInputBuffer in, boolean originalEncodedFormRetained)
+            throws IOException {
         // XXX must also parse BER-encoded constructed
         // values such as sequences, sets...
 
+        int startPosInInput = in.getPos();
         tag = (byte)in.read();
         byte lenByte = (byte)in.read();
         length = DerInputStream.getLength((lenByte & 0xff), in);
@@ -281,6 +289,11 @@
 
             in.skip(length);
         }
+
+        if (originalEncodedFormRetained) {
+            int consumed = in.getPos() - startPosInInput;
+            originalEncodedForm = in.getSlice(startPosInInput, consumed);
+        }
     }
 
     /**
@@ -822,6 +835,15 @@
     }
 
     /**
+     * Returns the original encoded form or {@code null} if the form was not
+     * retained or is not available.
+     */
+    public byte[] getOriginalEncodedForm() {
+        return (originalEncodedForm != null)
+                ? originalEncodedForm.clone() : null;
+    }
+
+    /**
      * Returns a DER-encoded value, such that if it's passed to the
      * DerValue constructor, a value equivalent to "this" is returned.
      *
diff --git a/ojluni/src/main/java/sun/security/x509/X509CertImpl.java b/ojluni/src/main/java/sun/security/x509/X509CertImpl.java
index 5ff1c9b..4f7f81b 100755
--- a/ojluni/src/main/java/sun/security/x509/X509CertImpl.java
+++ b/ojluni/src/main/java/sun/security/x509/X509CertImpl.java
@@ -315,6 +315,24 @@
     }
 
     /**
+     * Unmarshal a certificate from its encoded form, parsing a DER value.
+     * This form of constructor is used by agents which need to examine
+     * and use certificate contents.
+     *
+     * @param derVal the der value containing the encoded cert.
+     * @exception CertificateException on parsing and initialization errors.
+     */
+    public X509CertImpl(DerValue derVal, byte[] encoded)
+        throws CertificateException {
+        try {
+            parse(derVal, encoded);
+        } catch (IOException e) {
+            signedCert = null;
+            throw new CertificateException("Unable to initialize, " + e, e);
+        }
+    }
+
+    /**
      * Appends the certificate to an output stream.
      *
      * @param out an input stream to which the certificate is appended.
@@ -1770,6 +1788,24 @@
      */
     private void parse(DerValue val)
     throws CertificateException, IOException {
+        parse(
+            val,
+            null // use re-encoded form of val as the encoded form
+            );
+    }
+
+    /*
+     * Cert is a SIGNED ASN.1 macro, a three elment sequence:
+     *
+     *  - Data to be signed (ToBeSigned) -- the "raw" cert
+     *  - Signature algorithm (SigAlgId)
+     *  - The signature bits
+     *
+     * This routine unmarshals the certificate, saving the signature
+     * parts away for later verification.
+     */
+    private void parse(DerValue val, byte[] originalEncodedForm)
+    throws CertificateException, IOException {
         // check if can over write the certificate
         if (readOnly)
             throw new CertificateParsingException(
@@ -1779,7 +1815,9 @@
             throw new CertificateParsingException(
                       "invalid DER-encoded certificate data");
 
-        signedCert = val.toByteArray();
+        signedCert =
+                (originalEncodedForm != null)
+                        ? originalEncodedForm : val.toByteArray();
         DerValue[] seq = new DerValue[3];
 
         seq[0] = val.data.getDerValue();